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,215 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Lernen
5
+ # Graph represents a labelled directed graph.
6
+ #
7
+ # This is an intermediate data structure for rendering Mermaid and Graphviz diagrams.
8
+ class Graph
9
+ # @rbs!
10
+ # type node_shape = :circle | :doublecircle
11
+ #
12
+ # type mermaid_direction = "TD" | "DT" | "LR" | "RL"
13
+
14
+ # Node represents a node of graphs.
15
+ #
16
+ # @rbs skip
17
+ Node = Data.define(:label, :shape)
18
+
19
+ # @rbs!
20
+ # class Node < Data
21
+ # attr_reader label: String
22
+ # attr_reader shape: node_shape
23
+ # def self.[]: (String label, node_shape shape) -> Node
24
+ # end
25
+
26
+ # Edge represents an edge of graphs.
27
+ #
28
+ # @rbs skip
29
+ Edge = Data.define(:from, :label, :to)
30
+
31
+ # @rbs!
32
+ # class Edge < Data
33
+ # attr_reader from: Integer
34
+ # attr_reader label: String
35
+ # attr_reader to: Integer
36
+ # def self.[]: (Integer from, String label, Integer to) -> Edge
37
+ # end
38
+
39
+ # SubGraph represents a sub-graph of graphs.
40
+ #
41
+ # @rbs skip
42
+ SubGraph = Data.define(:label, :graph)
43
+
44
+ # @rbs!
45
+ # class SubGraph < Data
46
+ # attr_reader label: String
47
+ # attr_reader graph: Graph
48
+ # def self.[]: (String label, Graph graph) -> SubGraph
49
+ # end
50
+
51
+ # Returns the escaped string for Mermaid diagrams.
52
+ #
53
+ # See https://mermaid.js.org/syntax/flowchart.html#entity-codes-to-escape-characters.
54
+ #
55
+ #: (String label) -> String
56
+ def self.mermaid_escape(label)
57
+ label =
58
+ label.gsub(/[#"]/) do |c|
59
+ case c
60
+ in "#"
61
+ "#35;"
62
+ in "\""
63
+ "#quot;"
64
+ end
65
+ end
66
+ "\"#{label}\""
67
+ end
68
+
69
+ # Returns the escaped string for GraphViz DOTs.
70
+ #
71
+ # See https://graphviz.org/docs/attr-types/escString/.
72
+ #
73
+ #: (String label) -> String
74
+ def self.dot_escape(label)
75
+ label =
76
+ label.gsub(/[\\"]/) do |c|
77
+ case c
78
+ in "\\"
79
+ "\\\\"
80
+ in "\""
81
+ "\\\""
82
+ end
83
+ end
84
+ "\"#{label}\""
85
+ end
86
+
87
+ # @rbs @nodes: Hash[Integer, Node]
88
+ # @rbs @edges: Array[Edge]
89
+ # @rbs @subgraphs: Array[SubGraph]
90
+
91
+ #: (
92
+ # Hash[Integer, Node] nodes,
93
+ # Array[Edge] edges,
94
+ # ?Array[SubGraph] subgraphs
95
+ # ) -> void
96
+ def initialize(nodes, edges, subgraphs = [])
97
+ @nodes = nodes
98
+ @edges = edges
99
+ @subgraphs = subgraphs
100
+ end
101
+
102
+ attr_reader :nodes #: Hash[Integer, Node]
103
+ attr_reader :edges #: Array[Edge]
104
+ attr_reader :subgraphs #: Array[SubGraph]
105
+
106
+ # Returns a [Mermaid](https://mermaid.js.org) diagram of this graph.
107
+ #
108
+ #: (?direction: mermaid_direction) -> String
109
+ def to_mermaid(direction: "TD")
110
+ mmd = +""
111
+
112
+ mmd << "flowchart #{direction}\n"
113
+ mmd << to_mermaid_internal
114
+
115
+ mmd.freeze
116
+ end
117
+
118
+ # Returns a [GraphViz](https://graphviz.org) DOT diagram of this graph.
119
+ #
120
+ #: () -> String
121
+ def to_dot
122
+ dot = +""
123
+
124
+ dot << "digraph {\n"
125
+ dot << to_dot_internal
126
+ dot << "}\n"
127
+
128
+ dot.freeze
129
+ end
130
+
131
+ protected
132
+
133
+ #: (?String id_prefix) -> String
134
+ def to_mermaid_internal(id_prefix = "")
135
+ mmd = +""
136
+ needs_sep = false
137
+
138
+ nodes.each do |id, node|
139
+ needs_sep = true
140
+
141
+ node_def =
142
+ case node.shape
143
+ in :circle
144
+ "((#{Graph.mermaid_escape(node.label)}))"
145
+ in :doublecircle
146
+ "(((#{Graph.mermaid_escape(node.label)})))"
147
+ end
148
+ mmd << " #{id_prefix}#{id}#{node_def}\n"
149
+ end
150
+ mmd << "\n" if needs_sep
151
+
152
+ edges.each do |edge|
153
+ needs_sep = true
154
+
155
+ from = "#{id_prefix}#{edge.from}"
156
+ to = "#{id_prefix}#{edge.to}"
157
+ mmd << " #{from} -- #{Graph.mermaid_escape(edge.label)} --> #{to}\n"
158
+ end
159
+
160
+ subgraphs.each_with_index do |subgraph, index|
161
+ mmd << "\n" if needs_sep
162
+ needs_sep = true
163
+
164
+ subgraph_id = "#{id_prefix}g#{index}"
165
+ mmd << " subgraph #{subgraph_id}[#{Graph.mermaid_escape(subgraph.label)}]\n"
166
+ subgraph
167
+ .graph
168
+ .to_mermaid_internal("#{subgraph_id}_")
169
+ .lines
170
+ .each { |line| mmd << (line == "\n" ? line : " #{line}") }
171
+ mmd << " end\n"
172
+ end
173
+
174
+ mmd.freeze
175
+ end
176
+
177
+ #: (?String id_prefix) -> String
178
+ def to_dot_internal(id_prefix = "")
179
+ dot = +""
180
+ needs_sep = false
181
+
182
+ nodes.each do |index, node|
183
+ needs_sep = true
184
+
185
+ dot << " #{id_prefix}#{index} [label=#{Graph.dot_escape(node.label)}, shape=#{node.shape}];\n"
186
+ end
187
+ dot << "\n" if needs_sep
188
+
189
+ edges.each do |edge|
190
+ needs_sep = true
191
+
192
+ from = "#{id_prefix}#{edge.from}"
193
+ to = "#{id_prefix}#{edge.to}"
194
+ dot << " #{from} -> #{to} [label=#{Graph.dot_escape(edge.label)}];\n"
195
+ end
196
+
197
+ subgraphs.each_with_index do |subgraph, index|
198
+ dot << "\n" if needs_sep
199
+ needs_sep = true
200
+
201
+ subgraph_id = "#{id_prefix}g#{index}"
202
+ dot << " subgraph cluster_#{subgraph_id} {\n"
203
+ dot << " label=#{Graph.dot_escape(subgraph.label)};\n"
204
+ subgraph
205
+ .graph
206
+ .to_dot_internal("#{subgraph_id}_")
207
+ .lines
208
+ .each { |line| dot << (line == "\n" ? line : " #{line}") }
209
+ dot << " }\n"
210
+ end
211
+
212
+ dot.freeze
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Lernen
5
+ module System
6
+ # BlockSUL is a system under learning (SUL) constructed from a block.
7
+ #
8
+ # A block is expected to behave like a membership query.
9
+ #
10
+ # @rbs generic In -- Type for input alphabet
11
+ # @rbs generic Out -- Type for output values
12
+ class BlockSUL < MooreLikeSUL #[In, Out]
13
+ # @rbs @block: ^(Array[In]) -> Out
14
+ # @rbs @word: Array[In]
15
+
16
+ #: (?cache: bool) { (Array[In]) -> Out } -> void
17
+ def initialize(cache: true, &block)
18
+ super(cache:)
19
+
20
+ @block = block
21
+ @word = []
22
+ end
23
+
24
+ # @rbs override
25
+ def setup
26
+ @word = []
27
+ end
28
+
29
+ # @rbs override
30
+ def step(input)
31
+ @word << input
32
+ @block.call(@word)
33
+ end
34
+
35
+ # @rbs override
36
+ def query_empty
37
+ @block.call([])
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Lernen
5
+ module System
6
+ # MooreLikeSimulator is a system under learning (SUL) using a Moore-like machine simulation.
7
+ #
8
+ # @rbs generic Conf -- Type for a configuration of this automaton
9
+ # @rbs generic In -- Type for input alphabet
10
+ # @rbs generic Out -- Type for output values
11
+ class MooreLikeSimulator < MooreLikeSUL #[In, Out]
12
+ # @rbs @automaton: Automaton::MooreLike[Conf, In, Out]
13
+ # @rbs @current_conf: Conf | nil
14
+
15
+ #: (Automaton::MooreLike[Conf, In, Out] automaton, ?cache: bool) -> void
16
+ def initialize(automaton, cache: true)
17
+ super(cache:)
18
+
19
+ @automaton = automaton
20
+ @current_conf = nil
21
+ end
22
+
23
+ # @rbs override
24
+ def setup
25
+ @current_conf = @automaton.initial_conf
26
+ end
27
+
28
+ # @rbs override
29
+ def shutdown
30
+ @current_conf = nil
31
+ end
32
+
33
+ # @rbs override
34
+ def step(input)
35
+ output, @current_conf = @automaton.step(@current_conf, input) # steep:ignore
36
+ output
37
+ end
38
+
39
+ # @rbs override
40
+ def query_empty
41
+ @automaton.run_empty
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Lernen
5
+ module System
6
+ # MooreLikeSUL is a system under learning (SUL) for a system much like Moore machine.
7
+ #
8
+ # By contrast to `SUL`, this accepts a query with the empty string additionally.
9
+ #
10
+ # Note that this class is *abstract*. You should implement the following method:
11
+ #
12
+ # - `#step(input)`
13
+ # - `#query_empty`
14
+ #
15
+ # @rbs generic In -- Type for input alphabet
16
+ # @rbs generic Out -- Type for output values
17
+ class MooreLikeSUL < SUL #[In, Out]
18
+ #: (?cache: bool) -> void
19
+ def initialize(cache: true)
20
+ super
21
+ end
22
+
23
+ # Runs a membership query with the empty input.
24
+ #
25
+ # This is an abstract method.
26
+ #
27
+ #: () -> Out
28
+ def query_empty
29
+ raise TypeError, "abstract method: `query_empty`"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Lernen
5
+ module System
6
+ # SUL represents a system under learning.
7
+ #
8
+ # It is an abtraction of a system under learning (SUL) which accepts an operation
9
+ # called "membership query"; it takes an input string (word) and returns a sequence
10
+ # of outputs corresponding to the input string.
11
+ #
12
+ # This SUL assumes the system is much like Mealy machine; that is, a transition puts
13
+ # an output, and query does not accept the empty string due to no outputs.
14
+ #
15
+ # This SUL also implements cache mechanism. The cache mechanism is enabled by the default,
16
+ # and it can be disabled by specifying `cache: false`.
17
+ #
18
+ # On running a SUL, this SUL records the statistics information. We can obtain this
19
+ # information by `SUL#stats`.
20
+ #
21
+ # Note that this class is *abstract*. You should implement the following method:
22
+ #
23
+ # - `#step(input)`
24
+ #
25
+ # @rbs generic In -- Type for input alphabet
26
+ # @rbs generic Out -- Type for output values
27
+ class SUL
28
+ # @rbs @cache: Hash[Array[In], Array[Out]] | nil
29
+ # @rbs @num_cached_queries: Integer
30
+ # @rbs @num_queries: Integer
31
+ # @rbs @num_steps: Integer
32
+
33
+ #: (?cache: bool) -> void
34
+ def initialize(cache: true)
35
+ @cache = cache ? {} : nil
36
+ @num_cached_queries = 0
37
+ @num_queries = 0
38
+ @num_steps = 0
39
+ end
40
+
41
+ # Returns the statistics information as a `Hash` object.
42
+ #
43
+ # The result hash contains the following values.
44
+ #
45
+ # - `num_cache`: The number of cached queries.
46
+ # - `num_cached_queries`: The number of queries uses the cache.
47
+ # - `num_queries`: The number of (non-cached) queries.
48
+ # - `num_steps`: The total number of steps.
49
+ #
50
+ #: () -> Hash[Symbol, Integer]
51
+ def stats
52
+ {
53
+ num_cache: @cache&.size || 0,
54
+ num_cached_queries: @num_cached_queries,
55
+ num_queries: @num_queries,
56
+ num_steps: @num_steps
57
+ }
58
+ end
59
+
60
+ # Runs a membership query with the given word.
61
+ #
62
+ # Note that this method does not accept the empty string due to no outpus.
63
+ # If you need to call `query` with the empty string, you should use `MooreSUL#query_empty` instead.
64
+ #
65
+ #: (Array[In] word) -> Array[Out]
66
+ def query(word)
67
+ cached = (cache = @cache) && cache[word]
68
+ if cached
69
+ @num_cached_queries += 1
70
+ return cached
71
+ end
72
+
73
+ if word.empty?
74
+ raise ArgumentError, "`query` does not accept the empty string. Please use `query_empty` instead."
75
+ end
76
+
77
+ setup
78
+ outputs = word.map { |input| step(input) }
79
+ shutdown
80
+
81
+ @num_queries += 1
82
+ @num_steps += word.size
83
+
84
+ cache[word.dup] = outputs if cache
85
+
86
+ outputs
87
+ end
88
+
89
+ # Runs a membership query with the given word, and returns the last output.
90
+ #
91
+ # It is the same as `query(word).last`.
92
+ #
93
+ #: (Array[In] word) -> Out
94
+ def query_last(word) = query(word).last # steep:ignore
95
+
96
+ # rubocop:disable Lint/UnusedMethodArgument
97
+
98
+ # It is a setup procedure of this SUL.
99
+ #
100
+ # Note that it does nothing by default.
101
+ #
102
+ #: () -> void
103
+ def setup
104
+ end
105
+
106
+ # It is a shutdown procedure of this SUL.
107
+ #
108
+ # Note that it does nothing by default.
109
+ #
110
+ #: () -> void
111
+ def shutdown
112
+ end
113
+
114
+ # Consumes the given `input` and returns the correspoding output.
115
+ #
116
+ # This is an abstract method.
117
+ #
118
+ #: (In input) -> Out
119
+ def step(input)
120
+ raise TypeError, "abstract method: `step`"
121
+ end
122
+
123
+ # rubocop:enable Lint/UnusedMethodArgument
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Lernen
5
+ module System
6
+ # TransitionSystemSimulator is a system under learning (SUL) using a transition system simulation.
7
+ #
8
+ # @rbs generic Conf -- Type for a configuration of this automaton
9
+ # @rbs generic In -- Type for input alphabet
10
+ # @rbs generic Out -- Type for output values
11
+ class TransitionSystemSimulator < SUL #[In, Out]
12
+ # @rbs @automaton: Automaton::TransitionSystem[Conf, In, Out]
13
+ # @rbs @current_conf: Conf | nil
14
+
15
+ #: (Automaton::TransitionSystem[Conf, In, Out] automaton, ?cache: bool) -> void
16
+ def initialize(automaton, cache: true)
17
+ super(cache:)
18
+
19
+ @automaton = automaton
20
+ @current_conf = nil
21
+ end
22
+
23
+ # @rbs override
24
+ def setup
25
+ @current_conf = @automaton.initial_conf
26
+ end
27
+
28
+ # @rbs override
29
+ def shutdown
30
+ @current_conf = nil
31
+ end
32
+
33
+ # @rbs override
34
+ def step(input)
35
+ output, @current_conf = @automaton.step(@current_conf, input) # steep:ignore
36
+ output
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Lernen
5
+ # This is a namespace for systems under learning (SULs).
6
+ #
7
+ # `SUL` is an implementation of membership (or output) query.
8
+ #
9
+ # There are two base abstract classes of SULs: `SUL` and `MooreLikeSUL`
10
+ # `SUL` is the most base abstract class of SULs and it cannot answer a query
11
+ # with the empty string. `MooreLikeSUL` is subclass of `SUL` and it can answer
12
+ # the empty string query.
13
+ #
14
+ # In this library, we provide two easy ways to create a SUL. The first way is
15
+ # `System.from_block`. This method takes a block, and the block is used as
16
+ # a membership query implementation.
17
+ #
18
+ # ```ruby
19
+ # sul = Lernen::System.from_block do |word|
20
+ # word.count("1") % 4 == 3
21
+ # end
22
+ # ```
23
+ #
24
+ # The another way is `System.from_automaton`. This method takes an automaton
25
+ # (a transition system), and the result SUL simulates this automaton.
26
+ #
27
+ # ```ruby
28
+ # dfa = Lernen::Automaton::DFA.new(
29
+ # 0,
30
+ # Set[1],
31
+ # {
32
+ # [0, '0'] => 0,
33
+ # [0, '1'] => 1,
34
+ # [1, '0'] => 1,
35
+ # [1, '1'] => 1,
36
+ # }
37
+ # )
38
+ #
39
+ # sul = Lernen::System.from_automaton(dfa)
40
+ # ```
41
+ module System
42
+ # Creates a SUL from the given block as an implementation of a membership query.
43
+ #
44
+ #: [In, Out] (?cache: bool) { (Array[In]) -> Out } -> MooreLikeSUL[In, Out]
45
+ def self.from_block(cache: true, &) = BlockSUL.new(cache:, &)
46
+
47
+ # Creates a SUL from the given automaton as an implementation.
48
+ #
49
+ # This method chooses a suitable SUL implementation, which is one of `TransitionSystemSimulator`
50
+ # or `MooreLikeSimulator`, for the given automaton.
51
+ #
52
+ #: [In] (Automaton::DFA[In] automaton, ?cache: bool) -> SUL[In, bool]
53
+ #: [In, Out] (Automaton::Mealy[In, Out] automaton, ?cache: bool) -> SUL[In, Out]
54
+ #: [In, Out] (Automaton::Moore[In, Out] automaton, ?cache: bool) -> SUL[In, Out]
55
+ #: [In, Call, Return] (Automaton::VPA[In, Call, Return] automaton, ?cache: bool) -> SUL[In | Call | Return, bool]
56
+ #: [In, Call, Return] (Automaton::SPA[In, Call, Return] automaton, ?cache: bool) -> SUL[In | Call | Return, bool]
57
+ def self.from_automaton(automaton, cache: true) =
58
+ case automaton.type
59
+ in :dfa | :moore | :vpa | :spa
60
+ MooreLikeSimulator.new(automaton, cache:)
61
+ in :mealy
62
+ TransitionSystemSimulator.new(automaton, cache:)
63
+ end
64
+ end
65
+ end
66
+
67
+ require "lernen/system/sul"
68
+ require "lernen/system/moore_like_sul"
69
+
70
+ require "lernen/system/block_sul"
71
+ require "lernen/system/transition_system_simulator"
72
+ require "lernen/system/moore_like_simulator"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # rbs_inline: enabled
2
3
 
3
4
  module Lernen
4
5
  # The version string.
5
- VERSION = "0.2.0"
6
+ VERSION = "0.3.0"
6
7
  end