stamina 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/CHANGELOG.md +24 -0
  2. data/Gemfile.lock +5 -1
  3. data/bin/stamina +10 -0
  4. data/lib/stamina.rb +2 -1
  5. data/lib/stamina/abbadingo.rb +2 -0
  6. data/lib/stamina/abbadingo/random_dfa.rb +48 -0
  7. data/lib/stamina/abbadingo/random_sample.rb +146 -0
  8. data/lib/stamina/adl.rb +6 -6
  9. data/lib/stamina/automaton.rb +29 -4
  10. data/lib/stamina/automaton/complete.rb +36 -0
  11. data/lib/stamina/automaton/equivalence.rb +55 -0
  12. data/lib/stamina/automaton/metrics.rb +8 -1
  13. data/lib/stamina/automaton/minimize.rb +25 -0
  14. data/lib/stamina/automaton/minimize/hopcroft.rb +116 -0
  15. data/lib/stamina/automaton/minimize/pitchies.rb +64 -0
  16. data/lib/stamina/automaton/strip.rb +16 -0
  17. data/lib/stamina/automaton/walking.rb +46 -19
  18. data/lib/stamina/command.rb +45 -0
  19. data/lib/stamina/command/abbadingo_dfa.rb +81 -0
  20. data/lib/stamina/command/abbadingo_samples.rb +40 -0
  21. data/lib/stamina/command/adl2dot.rb +71 -0
  22. data/lib/stamina/command/classify.rb +48 -0
  23. data/lib/stamina/command/help.rb +27 -0
  24. data/lib/stamina/command/infer.rb +141 -0
  25. data/lib/stamina/command/metrics.rb +51 -0
  26. data/lib/stamina/command/robustness.rb +22 -0
  27. data/lib/stamina/command/score.rb +35 -0
  28. data/lib/stamina/errors.rb +4 -1
  29. data/lib/stamina/ext/math.rb +20 -0
  30. data/lib/stamina/induction/{redblue.rb → blue_fringe.rb} +29 -28
  31. data/lib/stamina/induction/commons.rb +32 -46
  32. data/lib/stamina/induction/rpni.rb +7 -9
  33. data/lib/stamina/induction/union_find.rb +3 -3
  34. data/lib/stamina/loader.rb +1 -0
  35. data/lib/stamina/sample.rb +79 -2
  36. data/lib/stamina/scoring.rb +37 -0
  37. data/lib/stamina/version.rb +2 -2
  38. data/stamina.gemspec +2 -1
  39. data/stamina.noespec +9 -12
  40. data/test/stamina/abbadingo/random_dfa_test.rb +16 -0
  41. data/test/stamina/abbadingo/random_sample_test.rb +78 -0
  42. data/test/stamina/adl_test.rb +27 -2
  43. data/test/stamina/automaton/complete_test.rb +58 -0
  44. data/test/stamina/automaton/equivalence_test.rb +120 -0
  45. data/test/stamina/automaton/minimize/hopcroft_test.rb +15 -0
  46. data/test/stamina/automaton/minimize/minimize_test.rb +55 -0
  47. data/test/stamina/automaton/minimize/pitchies_test.rb +15 -0
  48. data/test/stamina/automaton/minimize/rice_edu_10.adl +16 -0
  49. data/test/stamina/automaton/minimize/rice_edu_10.min.adl +13 -0
  50. data/test/stamina/automaton/minimize/rice_edu_13.adl +13 -0
  51. data/test/stamina/automaton/minimize/rice_edu_13.min.adl +7 -0
  52. data/test/stamina/automaton/minimize/should_strip_1.adl +8 -0
  53. data/test/stamina/automaton/minimize/should_strip_1.min.adl +6 -0
  54. data/test/stamina/automaton/minimize/unknown_1.adl +16 -0
  55. data/test/stamina/automaton/minimize/unknown_1.min.adl +12 -0
  56. data/test/stamina/automaton/strip_test.rb +36 -0
  57. data/test/stamina/automaton/walking/dfa_delta_test.rb +39 -0
  58. data/test/stamina/automaton_test.rb +13 -1
  59. data/test/stamina/induction/{redblue_test.rb → blue_fringe_test.rb} +22 -22
  60. data/test/stamina/sample_test.rb +75 -0
  61. data/test/stamina/stamina_test.rb +13 -2
  62. metadata +98 -23
  63. data/bin/adl2dot +0 -12
  64. data/bin/classify +0 -12
  65. data/bin/redblue +0 -12
  66. data/bin/rpni +0 -12
  67. data/lib/stamina/command/adl2dot_command.rb +0 -73
  68. data/lib/stamina/command/classify_command.rb +0 -57
  69. data/lib/stamina/command/redblue_command.rb +0 -58
  70. data/lib/stamina/command/rpni_command.rb +0 -58
  71. data/lib/stamina/command/stamina_command.rb +0 -79
@@ -171,6 +171,43 @@ module Stamina
171
171
  end
172
172
  alias :hbcr :harmonic_balanced_classification_rate
173
173
  alias :harmonic_bcr :harmonic_balanced_classification_rate
174
+
175
+ MEASURES = [
176
+ :false_positive, :false_negative,
177
+ :true_positive, :true_negative,
178
+ :accuracy, :error_rate,
179
+ :precision, :recall, :f_measure,
180
+ :false_positive_rate, :false_negative_rate,
181
+ :true_positive_rate, :true_negative_rate,
182
+ :positive_predictive_value, :negative_predictive_value,
183
+ :sensitivity, :specificity,
184
+ :positive_likelihood, :negative_likelihood,
185
+ :balanced_classification_rate, :balanced_error_rate, :harmonic_bcr
186
+ ]
187
+
188
+ def to_h
189
+ h = {}
190
+ MEASURES.each do |m|
191
+ h[m] = self.send(m.to_sym)
192
+ end
193
+ h
194
+ end
195
+
196
+ def to_s
197
+ s = ""
198
+ MEASURES.each do |m|
199
+ vals = case val = self.send(m.to_sym)
200
+ when Integer
201
+ "%s" % val
202
+ when Float
203
+ "%.5f" % val
204
+ else
205
+ "%s" % val
206
+ end
207
+ s += "%30s: %10s\n" % [m.to_s, vals]
208
+ end
209
+ s
210
+ end
174
211
 
175
212
  end # module Scoring
176
213
  end # module Stamina
@@ -2,8 +2,8 @@ module Stamina
2
2
  module Version
3
3
 
4
4
  MAJOR = 0
5
- MINOR = 3
6
- TINY = 1
5
+ MINOR = 4
6
+ TINY = 0
7
7
 
8
8
  def self.to_s
9
9
  [ MAJOR, MINOR, TINY ].join('.')
data/stamina.gemspec CHANGED
@@ -129,7 +129,8 @@ Gem::Specification.new do |s|
129
129
  s.add_development_dependency("yard", "~> 0.6.4")
130
130
  s.add_development_dependency("bluecloth", "~> 2.0.9")
131
131
  s.add_development_dependency("wlang", "~> 0.10.1")
132
-
132
+ s.add_development_dependency("gnuplot", "~> 2.3.6")
133
+ s.add_dependency("quickl", "~> 0.2.0")
133
134
 
134
135
  # The version of ruby required by this gem
135
136
  #
data/stamina.noespec CHANGED
@@ -9,7 +9,7 @@ variables:
9
9
  upper:
10
10
  Stamina
11
11
  version:
12
- 0.3.1
12
+ 0.4.0
13
13
  summary: |-
14
14
  Automaton and Regular Inference Toolkit
15
15
  description: |-
@@ -22,14 +22,11 @@ variables:
22
22
  - http://stamina.chefbe.net/
23
23
  - http://github.com/blambeau/stamina
24
24
  dependencies:
25
- # Rake is required for developers, as usual
26
- - {name: rake, version: "~> 0.8.7", groups: [development]}
27
- # Bundler is required for developers and is used by the Rakefile
28
- - {name: bundler, version: "~> 1.0", groups: [development]}
29
- # RSpec is required to run 'rake spec'. See tasks/spec.rake
30
- - {name: rspec, version: "~> 2.4.0", groups: [development]}
31
- # YARD and BlueCloth are required to run 'rake yard'. See tasks/yard.rake
32
- - {name: yard, version: "~> 0.6.4", groups: [development]}
33
- - {name: bluecloth, version: "~> 2.0.9", groups: [development]}
34
- # wlang is required to run 'rake debug_mail'. See tasks/debug_mail.rake
35
- - {name: wlang, version: "~> 0.10.1", groups: [development]}
25
+ - {name: quickl, version: "~> 0.2.0", groups: [runtime]}
26
+ - {name: rake, version: "~> 0.8.7", groups: [development]}
27
+ - {name: bundler, version: "~> 1.0", groups: [development]}
28
+ - {name: rspec, version: "~> 2.4.0", groups: [development]}
29
+ - {name: yard, version: "~> 0.6.4", groups: [development]}
30
+ - {name: bluecloth, version: "~> 2.0.9", groups: [development]}
31
+ - {name: wlang, version: "~> 0.10.1", groups: [development]}
32
+ - {name: gnuplot, version: "~> 2.3.6", groups: [development]}
@@ -0,0 +1,16 @@
1
+ require 'stamina/stamina_test'
2
+ require 'stamina/abbadingo'
3
+ module Stamina
4
+ module Abbadingo
5
+ class RandomDFATest < StaminaTest
6
+
7
+ def test_it_looks_ok_with_default_options
8
+ dfa = RandomDFA.new(32).execute
9
+ assert dfa.deterministic?
10
+ assert dfa.minimal?
11
+ assert dfa.complete?
12
+ end
13
+
14
+ end # class RandomDFATest
15
+ end # module Abbadingo
16
+ end # module Stamina
@@ -0,0 +1,78 @@
1
+ require 'stamina/stamina_test'
2
+ require 'stamina/abbadingo'
3
+ module Stamina
4
+ module Abbadingo
5
+ class RandomSampleTest < StaminaTest
6
+
7
+ def test_length_for
8
+ rs = RandomSample::StringEnumerator.new
9
+ assert_equal 0, rs.length_for(1)
10
+ assert_equal 1, rs.length_for(2)
11
+ assert_equal 1, rs.length_for(3)
12
+ assert_equal 2, rs.length_for(4)
13
+ assert_equal 2, rs.length_for(5)
14
+ assert_equal 2, rs.length_for(6)
15
+ assert_equal 2, rs.length_for(7)
16
+ assert_equal 3, rs.length_for(8)
17
+ end
18
+
19
+ def test_string_for
20
+ rs = RandomSample::StringEnumerator.new
21
+ assert_equal [], rs.string_for(1)
22
+ assert_equal ["0"], rs.string_for(2)
23
+ assert_equal ["1"], rs.string_for(3)
24
+ assert_equal ["0", "0"], rs.string_for(4)
25
+ assert_equal ["1", "0"], rs.string_for(5)
26
+ assert_equal ["0", "1"], rs.string_for(6)
27
+ assert_equal ["1", "1"], rs.string_for(7)
28
+ end
29
+
30
+ def test_string_for_generates_all_diff
31
+ rs = RandomSample::StringEnumerator.new
32
+ h = {}
33
+ (1..100).each{|i| h[rs.string_for(i)] = true}
34
+ assert_equal 100, h.size
35
+ end
36
+
37
+ def test_string_for_respects_distribution
38
+ rs = RandomSample::StringEnumerator.new
39
+ lengths = Hash.new{|h,k| h[k] = 0}
40
+ (1..127).each{|i| lengths[rs.string_for(i).size] += 1}
41
+ assert_equal [0, 1, 2, 3, 4, 5, 6], lengths.keys.sort
42
+ prop = (0..6).collect{|i| lengths[i].to_f/128}
43
+ assert_equal [0.0078125, 0.015625, 0.03125, 0.0625, 0.125, 0.25, 0.5], prop
44
+ end
45
+
46
+ def test_enumerator
47
+ enum = RandomSample::StringEnumerator.new(10)
48
+ lengths = Hash.new{|h,k| h[k] = 0}
49
+ 20000.times{lengths[enum.one.size] += 1}
50
+ assert (lengths.keys.sort - (0..10).to_a).empty?
51
+ prop = (0..10).collect{|i| lengths[i].to_f/20000}
52
+ assert((prop[-1] >= 0.45) && (prop[-1] <= 0.55))
53
+ assert((prop[-2] >= 0.2) && (prop[-2] <= 0.3))
54
+ assert((prop[-3] >= 0.1) && (prop[-3] <= 0.15))
55
+ end
56
+
57
+ def test_it_can_be_used_on_small_dfas
58
+ dfa = RandomDFA.new(16).execute
59
+ training, test = RandomSample.execute(dfa)
60
+
61
+ assert test.size > 0
62
+ assert training.size > 0
63
+
64
+ # check training sample
65
+ assert training.positive_count > 0
66
+ assert training.negative_count > 0
67
+ assert dfa.correctly_classify?(training)
68
+
69
+ # check test sample
70
+ assert test.positive_count > 0
71
+ assert test.negative_count > 0
72
+ assert dfa.correctly_classify?(test)
73
+ end
74
+
75
+
76
+ end # class RandomDFATest
77
+ end # module Abbadingo
78
+ end # module Stamina
@@ -486,6 +486,31 @@ module Stamina
486
486
  EOF
487
487
  end
488
488
  end
489
+
490
+ def test_allows_error_states
491
+ dfa = ADL::parse_automaton <<-EOF
492
+ 5 0
493
+ 0 true true true
494
+ 1 false false true
495
+ 2 false false false
496
+ 3 false true false
497
+ 4 false true
498
+ EOF
499
+ assert dfa.ith_state(0).accepting? && dfa.ith_state(0).error?
500
+ assert !dfa.ith_state(1).accepting? && dfa.ith_state(1).error?
501
+ assert !dfa.ith_state(2).accepting? && !dfa.ith_state(2).error?
502
+ assert dfa.ith_state(3).accepting? && !dfa.ith_state(3).error?
503
+ assert !dfa.ith_state(4).error?
504
+ end
505
+
506
+ def test_flushes_error_states
507
+ dfa = ADL::parse_automaton <<-EOF
508
+ 2 0
509
+ 0 true false
510
+ 1 false false true
511
+ EOF
512
+ assert_equal "1 false false true", dfa.to_adl.split("\n")[2].strip
513
+ end
514
+
489
515
  end # class ADLTest
490
-
491
- end # module Stamina
516
+ end # module Stamina
@@ -0,0 +1,58 @@
1
+ require 'test/unit'
2
+ require 'stamina/stamina_test'
3
+ module Stamina
4
+ class Automaton
5
+ class CompleteTest < StaminaTest
6
+
7
+ def test_on_not_complete
8
+ x, y, z = nil, nil, nil
9
+ dfa = Automaton.new(true) do |fa|
10
+ fa.alphabet = ["a", "b"]
11
+ x = fa.add_state(:initial => true, :accepting => true)
12
+ y = fa.add_state(:initial => false, :accepting => false)
13
+ fa.connect(0,1,'a')
14
+ fa.connect(1,0,'b')
15
+ end
16
+
17
+ assert_equal false, dfa.complete?
18
+ dfa.complete!
19
+ assert_equal true, dfa.complete?
20
+
21
+ assert_equal 3, dfa.state_count
22
+ z = dfa.ith_state(2)
23
+ assert_equal z, dfa.dfa_delta(x, "b")
24
+ assert_equal y, dfa.dfa_delta(x, "a")
25
+ assert_equal z, dfa.dfa_delta(y, "a")
26
+ assert_equal x, dfa.dfa_delta(y, "b")
27
+ end
28
+
29
+ def test_on_complete
30
+ dfa = Automaton.new(true) do |fa|
31
+ fa.alphabet = ["a"]
32
+ fa.add_state(:initial => true, :accepting => true)
33
+ fa.add_state(:initial => false, :accepting => false)
34
+ fa.connect(0,1,'a')
35
+ fa.connect(1,0,'a')
36
+ end
37
+ assert_equal true, dfa.complete?
38
+ dfa.complete!
39
+ assert_equal 2, dfa.state_count
40
+ end
41
+
42
+ def test_it_has_a_non_touching_impl
43
+ dfa = Automaton.new(true) do |fa|
44
+ fa.alphabet = ["a", "b"]
45
+ fa.add_state(:initial => true, :accepting => true)
46
+ fa.add_state(:initial => false, :accepting => false)
47
+ fa.connect(0,1,'a')
48
+ fa.connect(1,0,'b')
49
+ end
50
+ c = dfa.complete
51
+ assert_equal 2, dfa.state_count
52
+ assert_equal 3, c.state_count
53
+ end
54
+
55
+ end # class CompleteTest
56
+ end # class Automaton
57
+ end # module Stamina
58
+
@@ -0,0 +1,120 @@
1
+ require 'test/unit'
2
+ require 'stamina/stamina_test'
3
+ module Stamina
4
+ class Automaton
5
+ class EquivalenceTest < StaminaTest
6
+
7
+ def test_equivalence_on_small_dfa
8
+ assert_equal true, @small_dfa <=> @small_dfa
9
+ end
10
+
11
+ def test_equivalence_on_real_case
12
+ dfa1 = Stamina::ADL.parse_automaton <<-EOF
13
+ 3 5
14
+ 0 true false
15
+ 1 false false
16
+ 2 false true
17
+ 0 1 a
18
+ 1 1 a
19
+ 1 2 b
20
+ 2 2 b
21
+ 0 2 b
22
+ EOF
23
+ dfa2 = Stamina::ADL.parse_automaton <<-EOF
24
+ 3 5
25
+ 0 false true
26
+ 1 true false
27
+ 2 false false
28
+ 0 0 b
29
+ 1 2 a
30
+ 1 0 b
31
+ 2 2 a
32
+ 2 0 b
33
+ EOF
34
+ dfa3 = Stamina::ADL.parse_automaton <<-EOF
35
+ 3 5
36
+ 0 false false
37
+ 1 false true
38
+ 2 true false
39
+ 0 0 a
40
+ 0 1 b
41
+ 1 1 b
42
+ 2 0 a
43
+ 2 1 b
44
+ EOF
45
+ assert_equal true, dfa1 <=> dfa2
46
+ assert_equal true, dfa2 <=> dfa1
47
+ assert_equal true, dfa1 <=> dfa3
48
+ assert_equal true, dfa3 <=> dfa1
49
+ assert_equal true, dfa2 <=> dfa3
50
+ assert_equal true, dfa3 <=> dfa2
51
+ end
52
+
53
+ def test_equivalence_does_not_change_the_automata
54
+ dfa1 = Stamina::ADL.parse_automaton <<-EOF
55
+ 1 1
56
+ 0 true true
57
+ 0 0 a
58
+ EOF
59
+ assert_not_nil dfa1.initial_state
60
+ assert_equal true, dfa1 <=> dfa1
61
+ assert_not_nil dfa1.initial_state
62
+ end
63
+
64
+ def test_non_equivalent_dfa_are_recognized_1
65
+ dfa1 = Stamina::ADL.parse_automaton <<-EOF
66
+ 3 5
67
+ 0 true false
68
+ 1 false false
69
+ 2 false true
70
+ 0 1 a
71
+ 1 1 a
72
+ 1 2 b
73
+ 2 2 b
74
+ 0 2 b
75
+ EOF
76
+ assert_equal false, @small_dfa <=> dfa1
77
+ assert_equal false, dfa1 <=> @small_dfa
78
+ end
79
+
80
+ def test_non_equivalent_dfa_are_recognized_2
81
+ dfa1 = Stamina::ADL.parse_automaton <<-EOF
82
+ 5 4
83
+ 0 true false
84
+ 1 false false
85
+ 2 false false
86
+ 3 false false
87
+ 4 false false
88
+ 0 1 a
89
+ 1 2 a
90
+ 2 3 a
91
+ 3 4 a
92
+ EOF
93
+ dfa2 = Stamina::ADL.parse_automaton <<-EOF
94
+ 1 1
95
+ 0 true true
96
+ 0 0 a
97
+ EOF
98
+ assert_equal false, dfa2 <=> dfa1
99
+ assert_not_nil dfa1.initial_state
100
+ assert_not_nil dfa2.initial_state
101
+ assert_equal false, dfa1 <=> dfa2
102
+ end
103
+
104
+ def test_equivalence_takes_care_of_state_flags
105
+ dfa1 = Stamina::ADL.parse_automaton <<-EOF
106
+ 1 0
107
+ 0 true false
108
+ EOF
109
+ dfa2 = Stamina::ADL.parse_automaton <<-EOF
110
+ 1 0
111
+ 0 true true
112
+ EOF
113
+ assert_equal false, dfa1 <=> dfa2
114
+ assert_equal false, dfa2 <=> dfa1
115
+ end
116
+
117
+ end # class EquivalenceTest
118
+ end # class Automaton
119
+ end # module Stamina
120
+
@@ -0,0 +1,15 @@
1
+ require File.expand_path("../minimize_test", __FILE__)
2
+ module Stamina
3
+ class Automaton
4
+ module Minimize
5
+ class HopcroftTest < MinimizeTest
6
+
7
+ def algo
8
+ Hopcroft
9
+ end
10
+
11
+ end # class HopcroftTest
12
+ end # module Minimize
13
+ end # class Automaton
14
+ end # module Stamina
15
+
@@ -0,0 +1,55 @@
1
+ require 'test/unit'
2
+ require 'stamina/stamina_test'
3
+ module Stamina
4
+ class Automaton
5
+ module Minimize
6
+ class MinimizeTest < StaminaTest
7
+
8
+ # To be overriden
9
+ def algo
10
+ nil
11
+ end
12
+
13
+ def test_on_unknown_1
14
+ return unless algo
15
+ dfa = load_adl_automaton("unknown_1.adl", __FILE__)
16
+ min = load_adl_automaton("unknown_1.min.adl", __FILE__)
17
+ assert_equivalent(algo.execute(dfa), min)
18
+ end
19
+
20
+ # From slide 10 in http://www.clear.rice.edu/comp412/Lectures/L07DFAMin-1up.pdf
21
+ def test_on_rice_edu_10
22
+ return unless algo
23
+ dfa = load_adl_automaton("rice_edu_10.adl", __FILE__)
24
+ min = load_adl_automaton("rice_edu_10.min.adl", __FILE__)
25
+ assert_equivalent(algo.execute(dfa), min)
26
+ end
27
+
28
+ # From slide 13 in http://www.clear.rice.edu/comp412/Lectures/L07DFAMin-1up.pdf
29
+ def test_on_rice_edu_13
30
+ return unless algo
31
+ dfa = load_adl_automaton("rice_edu_13.adl", __FILE__)
32
+ min = load_adl_automaton("rice_edu_13.min.adl", __FILE__)
33
+ assert_equivalent(algo.execute(dfa), min)
34
+ end
35
+
36
+ def test_it_strips_when_needed
37
+ return unless algo
38
+ dfa = load_adl_automaton("should_strip_1.adl", __FILE__)
39
+ min = load_adl_automaton("should_strip_1.min.adl", __FILE__)
40
+ assert_equivalent(algo.execute(dfa), min)
41
+ end
42
+
43
+ def test_it_has_no_effect_on_already_minimal
44
+ return unless algo
45
+ dfa = load_adl_automaton("rice_edu_13.min.adl", __FILE__)
46
+ min = algo.execute(dfa)
47
+ assert_equal dfa.complete.state_count, min.complete.state_count
48
+ assert_equivalent(min, dfa)
49
+ end
50
+
51
+ end # class MinimizeTest
52
+ end # module Minimize
53
+ end # class Automaton
54
+ end # module Stamina
55
+