art-decomp 0.0.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 (84) hide show
  1. data/.gitignore +2 -0
  2. data/LICENCE +661 -0
  3. data/README +9 -0
  4. data/Rakefile +46 -0
  5. data/VERSION +1 -0
  6. data/bin/ad-compare +22 -0
  7. data/bin/ad-console +11 -0
  8. data/bin/ad-inputs +24 -0
  9. data/bin/ad-validate +9 -0
  10. data/bin/art-decomp +8 -0
  11. data/lib/art-decomp/arch.rb +32 -0
  12. data/lib/art-decomp/b.rb +7 -0
  13. data/lib/art-decomp/bipainter.rb +142 -0
  14. data/lib/art-decomp/blanket.rb +82 -0
  15. data/lib/art-decomp/decomposer.rb +75 -0
  16. data/lib/art-decomp/decomposition.rb +91 -0
  17. data/lib/art-decomp/exceptions.rb +9 -0
  18. data/lib/art-decomp/executable.rb +94 -0
  19. data/lib/art-decomp/fsm.rb +128 -0
  20. data/lib/art-decomp/graph.rb +78 -0
  21. data/lib/art-decomp/kiss.rb +47 -0
  22. data/lib/art-decomp/logging.rb +52 -0
  23. data/lib/art-decomp/qu_generator/block_table.rb +42 -0
  24. data/lib/art-decomp/qu_generator/edge_labels.rb +24 -0
  25. data/lib/art-decomp/qv_generator/bipainting.rb +12 -0
  26. data/lib/art-decomp/qv_generator/graph_colouring.rb +16 -0
  27. data/lib/art-decomp/qv_generator/graph_merging.rb +19 -0
  28. data/lib/art-decomp/sep.rb +7 -0
  29. data/lib/art-decomp/uv_generator/braindead.rb +22 -0
  30. data/lib/art-decomp/uv_generator/relevance.rb +23 -0
  31. data/lib/art-decomp.rb +65 -0
  32. data/lib/core/enumerable.rb +24 -0
  33. data/lib/core/file.rb +11 -0
  34. data/lib/core/integer.rb +13 -0
  35. data/lib/core/set.rb +16 -0
  36. data/lib/core/string.rb +13 -0
  37. data/spec/art-decomp/arch_spec.rb +23 -0
  38. data/spec/art-decomp/b_spec.rb +27 -0
  39. data/spec/art-decomp/bipainter_spec.rb +22 -0
  40. data/spec/art-decomp/blanket_spec.rb +77 -0
  41. data/spec/art-decomp/decomposer_spec.rb +106 -0
  42. data/spec/art-decomp/decomposition_spec.rb +119 -0
  43. data/spec/art-decomp/executable_spec.rb +161 -0
  44. data/spec/art-decomp/fsm_spec.rb +146 -0
  45. data/spec/art-decomp/graph_spec.rb +69 -0
  46. data/spec/art-decomp/kiss_spec.rb +30 -0
  47. data/spec/art-decomp/logging_spec.rb +62 -0
  48. data/spec/art-decomp/qu_generator/block_table_spec.rb +37 -0
  49. data/spec/art-decomp/qu_generator/edge_labels_spec.rb +35 -0
  50. data/spec/art-decomp/qv_generator/bipainting_spec.rb +28 -0
  51. data/spec/art-decomp/qv_generator/graph_colouring_spec.rb +31 -0
  52. data/spec/art-decomp/qv_generator/graph_merging_spec.rb +30 -0
  53. data/spec/art-decomp/sep_spec.rb +13 -0
  54. data/spec/art-decomp/uv_generator/braindead_spec.rb +33 -0
  55. data/spec/art-decomp/uv_generator/relevance_spec.rb +61 -0
  56. data/spec/core/enumerable_spec.rb +20 -0
  57. data/spec/core/file_spec.rb +15 -0
  58. data/spec/core/integer_spec.rb +16 -0
  59. data/spec/core/set_spec.rb +26 -0
  60. data/spec/core/string_spec.rb +17 -0
  61. data/spec/fixtures/ex5 +36 -0
  62. data/spec/fixtures/fsm +24 -0
  63. data/spec/fixtures/fsm.exp +40 -0
  64. data/spec/fixtures/fsm.f +20 -0
  65. data/spec/fixtures/fsm.g +4 -0
  66. data/spec/fixtures/fsm.h +19 -0
  67. data/spec/fixtures/fsm.partially-exp +31 -0
  68. data/spec/fixtures/fsm.q +10 -0
  69. data/spec/fixtures/lion +15 -0
  70. data/spec/fixtures/lion.exp +15 -0
  71. data/spec/fixtures/lion.h +10 -0
  72. data/spec/fixtures/lion.to_kiss +11 -0
  73. data/spec/fixtures/mark1 +26 -0
  74. data/spec/fixtures/mc +14 -0
  75. data/spec/fixtures/mc.to_kiss +10 -0
  76. data/spec/fixtures/opus +26 -0
  77. data/spec/fixtures/opus.amb.h +22 -0
  78. data/spec/fixtures/opus.h +21 -0
  79. data/spec/fixtures/opus.to_kiss +22 -0
  80. data/spec/fixtures/s420 +142 -0
  81. data/spec/fixtures/s8 +24 -0
  82. data/spec/spec.opts +3 -0
  83. data/spec/spec_helper.rb +8 -0
  84. metadata +224 -0
@@ -0,0 +1,22 @@
1
+ module ArtDecomp describe Bipainter do
2
+
3
+ context 'when used for generating the Qv and G blankets' do
4
+
5
+ # FIXME: add more specs, targeting non-trivial cases
6
+
7
+ it 'should colour the two incompatibility graphs properly and return the resulting blankets' do
8
+ beta_q = Blanket[B[1,2], B[3,4], B[5,6]]
9
+ beta_v = Blanket[B[1,3,5], B[2,4,6]]
10
+ seps = Set[Sep[1,2], Sep[1,3], Sep[1,6], Sep[2,6], Sep[3,4], Sep[3,6], Sep[4,5], Sep[5,6]]
11
+
12
+ bipainter = Bipainter.new beta_q, beta_v, seps
13
+ bipainter.blankets.should == [Blanket[B[1,2], B[3,4,5,6]], Blanket[B[1], B[2,3,5], B[4,6]]]
14
+ end
15
+
16
+ it 'should raise a RuntimeError on non-disjoint beta_v' do
17
+ lambda { Bipainter.new Blanket[], Blanket[B[0,1], B[0,2]], Set[] }.should raise_error(RuntimeError, 'non-disjoint beta_v')
18
+ end
19
+
20
+ end
21
+
22
+ end end
@@ -0,0 +1,77 @@
1
+ # encoding: UTF-8
2
+
3
+ module ArtDecomp describe Blanket do
4
+
5
+ it 'should be easily instantiable, properly comparable and usable as a Set element' do
6
+ Blanket[B[], B[0], B[1,2]].should == Blanket.new([B[1,2], B[0]])
7
+ Blanket[B[], B[0], B[1,2]].hash.should == Blanket.new([B[1,2], B[0]]).hash
8
+ Blanket[B[], B[0], B[1,2]].should eql Blanket.new([B[1,2], B[0]])
9
+ end
10
+
11
+ it 'should expose its internal ints variable and it should be immutable' do
12
+ Blanket[B[1,2], B[3,4]].ints.should == Set[B[3,4], B[1,2]]
13
+ Blanket[B[1,2], B[3,4]].ints.should be_frozen
14
+ end
15
+
16
+ it 'should be properly instantiable from an Array' do
17
+ blanket = Blanket.from_array [:b, DontCare, :a, :c, :b, :a, :c]
18
+ blanket.should == Blanket[B[0,1,4], B[1,2,5], B[1,3,6]]
19
+ end
20
+
21
+ it 'should be inspectable and return a self-instanting form' do
22
+ Blanket[B[]].inspect.should == 'Blanket[]'
23
+ Blanket[B[1,2], B[0]].inspect.should == 'Blanket[B[0], B[1,2]]'
24
+ end
25
+
26
+ it 'should be multipliable by other Blankets' do
27
+ b1 = Blanket[B[1,2,3], B[4,5,6]]
28
+ b2 = Blanket[B[1,2], B[3,4], B[5,6]]
29
+ (b1 * b2).should == Blanket[B[1,2], B[3], B[4], B[5,6]]
30
+ end
31
+
32
+ it 'should properly expose the separations it provides/requires' do
33
+ Blanket[].seps.should == Set[]
34
+ Blanket[B[1]].seps.should == Set[]
35
+ Blanket[B[1], B[2], B[]].seps.should == Set[Sep[1,2]]
36
+ Blanket[B[1,2], B[3,4]].seps.should == Set[Sep[1,3], Sep[1,4], Sep[2,3], Sep[2,4]]
37
+ Blanket[B[1,2,3], B[2,3,4]].seps.should == Set[Sep[1,4]]
38
+ end
39
+
40
+ it 'should properly report its size' do
41
+ Blanket[].size.should == 0
42
+ Blanket[B[1]].size.should == 1
43
+ Blanket[B[1], B[2], B[]].size.should == 2
44
+ Blanket[B[1,2,3], B[2,3,4]].size.should == 2
45
+ end
46
+
47
+ it 'should return the Blanket’s ‘natural’ encoding, based on the provided subblock' do
48
+ blanket = Blanket[B[0,1,2], B[1,2,3], B[2,3,4]]
49
+ blanket.encoding(B[0,1,2]).should == '00'
50
+ blanket.encoding(B[1,2,3]).should == '01'
51
+ blanket.encoding(B[2,3,4]).should == '10'
52
+ blanket.encoding(B[2]).should == '--'
53
+ lambda { blanket.encoding(B[1,2]) }.should raise_error(AmbiguousEncodingQuery, 'ambiguous encoding query: block 1,2')
54
+ end
55
+
56
+ it 'should return the Blanket’s ‘natural’ encodings in an Array if so asked' do
57
+ blanket = Blanket[B[0,1,2], B[1,2,3], B[2,3,4]]
58
+ blanket.encodings(B[0,1,2]).should == ['00']
59
+ blanket.encodings(B[1,2,3]).should == ['01']
60
+ blanket.encodings(B[2,3,4]).should == ['10']
61
+ blanket.encodings(B[2]).should == ['--']
62
+ blanket.encodings(B[1,2]).should == ['00', '01']
63
+ end
64
+
65
+ it 'should report how many physical pins it uses' do
66
+ Blanket[B[]].pins.should == 0
67
+ Blanket[B[1]].pins.should == 0
68
+ Blanket[B[1,2]].pins.should == 0
69
+ Blanket[B[1],B[2]].pins.should == 1
70
+ Blanket[B[1],B[2],B[3]].pins.should == 2
71
+ Blanket[B[1,2],B[3,4],B[5,6],B[7]].pins.should == 2
72
+ Blanket[B[1],B[2],B[3],B[4],B[5]].pins.should == 3
73
+ Blanket[B[1],B[2],B[3],B[4],B[5],B[6],B[7],B[8]].pins.should == 3
74
+ Blanket[B[1],B[2],B[3],B[4],B[5],B[6],B[7],B[8],B[9]].pins.should == 4
75
+ end
76
+
77
+ end end
@@ -0,0 +1,106 @@
1
+ module ArtDecomp describe Decomposer do
2
+
3
+ it 'should instantiate itself and its components properly' do
4
+ fsm = mock FSM
5
+ archs = Set[Arch[5,1], Arch[4,2]]
6
+ uv1, uv2 = mock('UVGenerator class'), mock('UVGenerator class')
7
+ qu1, qu2 = mock('QuGenerator class'), mock('QuGenerator class')
8
+ qv1, qv2 = mock('QvGenerator class'), mock('QvGenerator class')
9
+ [uv1, uv2].each { |gen| gen.should_receive(:new).with fsm, archs }
10
+ [qu1, qu2, qv1, qv2].each { |gen| gen.should_receive(:new).with no_args }
11
+ Decomposer.new :fsm => fsm, :archs => archs, :uv_gens => [uv1, uv2], :qu_gens => [qu1, qu2], :qv_gens => [qv1, qv2]
12
+ end
13
+
14
+ context 'given that the three generators are working properly' do
15
+
16
+ class StubGenerator
17
+ def initialize sequences
18
+ @sequences = sequences
19
+ end
20
+ def elems *key, &block
21
+ @sequences[key].each &block
22
+ end
23
+ alias uv_pairs elems
24
+ alias blankets elems
25
+ end
26
+
27
+ it 'should poll the generators and yield the resulting decompositions one by one' do
28
+ fsm = mock FSM, :beta_q => mock(Blanket, :pins => 3, :size => 5), :input_count => 4
29
+
30
+ u_a, v_a = Set[0,1], Set[2] # for this U/V pair: two Qu generating one Qv/G pair each
31
+ qu_a1, qv_a1, g_a1 = mock(Blanket, :pins => 2, :size => 4), mock(Blanket, :pins => 3, :size => 5), mock(Blanket, :pins => 2)
32
+ qu_a2, qv_a2, g_a2 = mock(Blanket, :pins => 2, :size => 4), mock(Blanket, :pins => 3, :size => 5), mock(Blanket, :pins => 2)
33
+
34
+ u_b, v_b = Set[0], Set[1,2] # for this U/V pair: one Qu generating two Qv/G pairs
35
+ qu_b = mock Blanket, :pins => 2, :size => 4
36
+ qv_bA, g_bA = mock(Blanket, :pins => 3, :size => 5), mock(Blanket, :pins => 2)
37
+ qv_bB, g_bB = mock(Blanket, :pins => 3, :size => 5), mock(Blanket, :pins => 2)
38
+
39
+ uv_gen = mock UVGenerator, :new => StubGenerator.new({[] => [[fsm, u_a, v_a], [fsm, u_b, v_b]]})
40
+ qu_gen = mock QuGenerator, :new => StubGenerator.new({[fsm, u_a, v_a] => [qu_a1, qu_a2],
41
+ [fsm, u_b, v_b] => [qu_b]})
42
+ qv_gen = mock QvGenerator, :new => StubGenerator.new({[fsm, u_a, v_a, qu_a1] => [[qv_a1, g_a1]],
43
+ [fsm, u_a, v_a, qu_a2] => [[qv_a2, g_a2]],
44
+ [fsm, u_b, v_b, qu_b] => [[qv_bA, g_bA], [qv_bB, g_bB]]})
45
+
46
+ decomposer = Decomposer.new :archs => Set[Arch[5,1]], :fsm => fsm, :uv_gens => [uv_gen], :qu_gens => [qu_gen], :qv_gens => [qv_gen]
47
+ results = decomposer.decompositions.to_a
48
+ results.size.should == 4
49
+ results.first.should == Decomposition.new(fsm, u_a, v_a, qu_a1, qv_a1, g_a1)
50
+ results.last.should == Decomposition.new(fsm, u_b, v_b, qu_b, qv_bB, g_bB)
51
+ end
52
+
53
+ it 'should yield only sensible decompositions' do
54
+ fsm = mock FSM
55
+ uv_gen = mock UVGenerator, :uv_pairs => [[fsm, Set[0], Set[1]]]
56
+ qu_gen = mock QuGenerator, :blankets => [mock(Blanket)]
57
+ qv_gen = mock QvGenerator, :blankets => [[mock(Blanket), mock(Blanket)], [mock(Blanket), mock(Blanket)]]
58
+ dec1 = mock Decomposition, :sensible? => true
59
+ dec2 = mock Decomposition, :sensible? => false
60
+ Decomposition.should_receive(:new).exactly(2).times.and_return dec1, dec2
61
+ decomposer = Decomposer.new :fsm => fsm, :uv_gens => [mock('UVG', :new => uv_gen)], :qu_gens => [mock('QuG', :new => qu_gen)], :qv_gens => [mock('QvG', :new => qv_gen)]
62
+ decomposer.decompositions.to_a.should == [dec1]
63
+ end
64
+
65
+ it 'should skip re-computing elements it already computed once (unless told not to)' do
66
+ qu1, qu2, qv, g1, g2 = mock(Blanket), mock(Blanket), mock(Blanket), mock(Blanket), mock(Blanket)
67
+ fsm = mock FSM
68
+ uv_gen = mock UVGenerator, :uv_pairs => [[fsm, Set[0], Set[1]], [fsm, Set[0], Set[1]], [fsm, Set[1], Set[0]]]
69
+ qu_gen = mock QuGenerator, :blankets => [qu1, qu1, qu2]
70
+ qv_gen = mock QvGenerator, :blankets => [[qv, g1], [qv, g1], [qv, g2]]
71
+ dec = mock Decomposition, :sensible? => true
72
+ Decomposition.should_receive(:new).exactly(8).times.and_return dec
73
+ decomposer = Decomposer.new :fsm => fsm, :uv_gens => [mock('UVG', :new => uv_gen)], :qu_gens => [mock('QuG', :new => qu_gen)], :qv_gens => [mock('QvG', :new => qv_gen)]
74
+ decomposer.decompositions.to_a.size.should == 8
75
+ Decomposition.should_receive(:new).exactly(27).times.and_return dec
76
+ decomposer = Decomposer.new :fsm => fsm, :uv_gens => [mock('UVG', :new => uv_gen)], :qu_gens => [mock('QuG', :new => qu_gen)], :qv_gens => [mock('QvG', :new => qv_gen)]
77
+ decomposer.decompositions(:keep_seen => true).to_a.size.should == 27
78
+ end
79
+
80
+ it 'should compute shallow ((u & v).size == 1) non-disjoint decompositions (if told to)' do
81
+ qu, qv, g1, g2 = mock(Blanket), mock(Blanket), mock(Blanket, :pins => 1), mock(Blanket, :pins => 2)
82
+ fsm = mock FSM
83
+ uv_gen = mock UVGenerator, :uv_pairs => [[fsm, Set[0,1], Set[2,3]]]
84
+ qu_gen = mock QuGenerator, :blankets => [qu]
85
+ qv_gen = mock QvGenerator, :blankets => [[qv, g2], [qv, g1]]
86
+ dec = mock Decomposition, :sensible? => true
87
+ Decomposition.should_receive(:new).exactly(6).times.and_return dec
88
+ decomposer = Decomposer.new :fsm => fsm, :uv_gens => [mock('UVG', :new => uv_gen)], :qu_gens => [mock('QuG', :new => qu_gen)], :qv_gens => [mock('QvG', :new => qv_gen)]
89
+ decomposer.decompositions(:non_disjoint => true).to_a.size.should == 4
90
+ end
91
+
92
+ it 'should compute deep ((u & v).size > 1) non-disjoint decompositions (if told to)' do
93
+ qu, qv, g1, g2 = mock(Blanket), mock(Blanket), mock(Blanket, :pins => 1), mock(Blanket, :pins => 2)
94
+ fsm = mock FSM
95
+ uv_gen = mock UVGenerator, :uv_pairs => [[fsm, Set[0,1], Set[2,3]]]
96
+ qu_gen = mock QuGenerator, :blankets => [qu]
97
+ qv_gen = mock QvGenerator, :blankets => [[qv, g2], [qv, g1]]
98
+ dec = mock Decomposition, :sensible? => true
99
+ Decomposition.should_receive(:new).exactly(8).times.and_return dec
100
+ decomposer = Decomposer.new :fsm => fsm, :uv_gens => [mock('UVG', :new => uv_gen)], :qu_gens => [mock('QuG', :new => qu_gen)], :qv_gens => [mock('QvG', :new => qv_gen)]
101
+ decomposer.decompositions(:non_disjoint => true, :deep_ndj => true).to_a.size.should == 4
102
+ end
103
+
104
+ end
105
+
106
+ end end
@@ -0,0 +1,119 @@
1
+ # encoding: UTF-8
2
+
3
+ module ArtDecomp describe Decomposition do
4
+
5
+ it 'should instantiate, compare and hash properly' do
6
+ fsm = mock FSM
7
+ u, v = Set[0,1], Set[2]
8
+ qu, qv, g = mock(Blanket), mock(Blanket), mock(Blanket)
9
+ dec = Decomposition.new fsm, u, v, qu, qv, g
10
+ dec.should == Decomposition.new(fsm, u, v, qu, qv, g)
11
+ dec.should_not == Decomposition.new(fsm, v, u, qu, qv, g)
12
+ dec.should_not == Decomposition.new(fsm, u, v, qv, qu, g)
13
+ dec.hash.should == Decomposition.new(fsm, u, v, qu, qv, g).hash
14
+ dec.should eql Decomposition.new(fsm, u, v, qu, qv, g)
15
+ end
16
+
17
+ it 'should create its F, Q, G and H blocks’ KISS representations properly' do
18
+ fsm = FSM.from_kiss 'spec/fixtures/fsm'
19
+ qu = Blanket[B[0,4,5], B[1,2,3,13,14], B[6,7,8,9,10,11,12], B[15,16,17,18,19]]
20
+ qv = Blanket[B[0,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,19], B[1,2,18]]
21
+ g = Blanket[B[0,2,5,6,7,9,11,15,16,17,18,19], B[1,3,4,8,10,12,13,14]]
22
+ decomposition = Decomposition.new fsm, Set[0,1,3], Set[2], qu, qv, g
23
+ decomposition.f_kiss.should == File.read('spec/fixtures/fsm.f')
24
+ decomposition.q_kiss.should == File.read('spec/fixtures/fsm.q')
25
+ decomposition.g_kiss.should == File.read('spec/fixtures/fsm.g')
26
+ decomposition.h_kiss.should == File.read('spec/fixtures/fsm.h')
27
+ end
28
+
29
+ it 'should create the H block properly, even if the G blanket seems ambiguous' do
30
+ fsm = FSM.from_kiss 'spec/fixtures/opus'
31
+ qu = Blanket[B[0,1,11,14,15,16,17,18,19,20,30,33,34,35,36,37], B[2,5,7,14,21,24,26,33], B[3,4,10,13,14,22,23,29,32,33], B[6,8,9,12,14,25,27,28,31,33]]
32
+ qv = Blanket[B[0,2,6,8,10,13,14,15,19,21,25,27,29,32,33,34], B[1,3,4,5,7,9,12,14,20,22,23,24,26,28,31,33], B[11,14,16,17,18,30,33,35,36,37]]
33
+ g = Blanket[B[0,2,6,8,10,13,14,15,19,21,25,27,29,32,33,34], B[1,3,4,5,7,9,12,14,20,22,23,24,26,28,31,33], B[11,14,16,17,18], B[30,33,35,36,37]]
34
+ decomposition = Decomposition.new fsm.expand_x(Set[0]), Set[1,2,3,4], Set[0], qu, qv, g
35
+ decomposition.h_kiss.should == File.read('spec/fixtures/opus.amb.h')
36
+ end
37
+
38
+ it 'should properly recode don’t-care states to *' do
39
+ fsm = FSM.from_kiss 'spec/fixtures/opus'
40
+ qu = Blanket[B[0,1,2,3,4,20,21], B[0,5,6,7], B[0,8,9,10,11,12,13,14,15,16], B[0,17,18,19]]
41
+ qv = Blanket[B[0,1,2,6,7,15,16,17,18], B[0,3,4,5,8,9,10,11,12,13,14,19], B[0,20,21]]
42
+ g = Blanket[B[0,1], B[2,6,7,15,16,17,18], B[3,4,5,8,9,10,11,12,13,14,19], B[20,21]]
43
+ decomposition = Decomposition.new fsm, Set[0,1,3,4], Set[2], qu, qv, g
44
+ decomposition.h_kiss.should == File.read('spec/fixtures/opus.h')
45
+ end
46
+
47
+ it 'should properly report whether it’s valid' do
48
+ fsm = FSM.from_kiss 'spec/fixtures/fsm'
49
+ qu = Blanket[B[0,1,2,3,4,5,17], B[6,7,8,9,10,11,12,13,14,15,16,18,19]]
50
+ qv = Blanket[B[0,1,2,17,19], B[3,6,7,8,9,10,11,12], B[4,5,18], B[13,14,15,16]]
51
+ g = Blanket[B[0,1,4,6,17,18,19], B[2,7,9,11], B[3,5,8,10,12], B[13,14,15,16]]
52
+ Decomposition.new(fsm, Set[1,3], Set[0,2], qu, qv, g).should be_valid
53
+ Decomposition.new(fsm, Set[1,3], Set[0,2], qv, qu, g).should_not be_valid
54
+ Decomposition.new(fsm, Set[1,3], Set[0,2], qv, qv, g).should_not be_valid
55
+ end
56
+
57
+ it 'should report whether it’s disjoint' do
58
+ fsm = mock FSM
59
+ b = mock Blanket
60
+ Decomposition.new(fsm, Set[0], Set[1], b, b, b).should be_disjoint
61
+ Decomposition.new(fsm, Set[0,1], Set[1], b, b, b).should_not be_disjoint
62
+ Decomposition.new(fsm, Set[1], Set[0,1], b, b, b).should_not be_disjoint
63
+ end
64
+
65
+ context 'when assesing whether it’s usable in the future' do
66
+
67
+ before do
68
+ @fsm = mock FSM, :beta_q => mock(Blanket, :pins => 3), :input_count => 3, :output_count => 2
69
+ @b = mock Blanket
70
+ @b1 = mock Blanket, :pins => 1, :size => 2
71
+ @b2 = mock Blanket, :pins => 2, :size => 4
72
+ @b3 = mock Blanket, :pins => 3
73
+ @b4 = mock Blanket, :pins => 4
74
+ end
75
+
76
+ it 'should properly report whether it’s decomposable further' do
77
+ Decomposition.new(@fsm, Set[0], Set[1], @b1, @b, @b).should_not be_decomposable
78
+ Decomposition.new(@fsm, Set[0], Set[1], @b2, @b, @b).should be_decomposable
79
+ end
80
+
81
+ it 'should properly report whether it’s sensible, based on the target Archs and G and H blocks’ architectures' do
82
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b2, @b2, @b2).should_not be_sensible Set[Arch[3,2]]
83
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b2, @b2, @b2).should be_sensible Set[Arch[4,1]]
84
+ Decomposition.new(@fsm, Set[], Set[0,1,2], @b2, @b2, @b2).should_not be_sensible Set[Arch[4,1]]
85
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b2, @b3, @b2).should_not be_sensible Set[Arch[4,1]]
86
+ Decomposition.new(@fsm, Set[0,1], Set[1,2], @b2, @b2, @b2).should_not be_sensible Set[Arch[4,1]]
87
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b3, @b2, @b2).should_not be_sensible Set[Arch[4,1]]
88
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b2, @b2, @b3).should_not be_sensible Set[Arch[4,1]]
89
+ end
90
+
91
+ it 'should properly report whether it’s final based on a Set of Archs' do
92
+ Decomposition.new(@fsm, Set[0], Set[1], @b1, @b3, @b3).should be_final Set[Arch[5,1]]
93
+ Decomposition.new(@fsm, Set[0], Set[1], @b1, @b3, @b3).should_not be_final Set[Arch[4,2]]
94
+ Decomposition.new(@fsm, Set[], Set[1], @b1, @b3, @b3).should be_final Set[Arch[4,2]]
95
+ Decomposition.new(@fsm, Set[], Set[1,2], @b1, @b3, @b3).should_not be_final Set[Arch[4,2]]
96
+ Decomposition.new(@fsm, Set[], Set[1], @b1, @b4, @b3).should_not be_final Set[Arch[4,2]]
97
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b1, @b3, @b3).should be_final Set[Arch[5,1]]
98
+ Decomposition.new(@fsm, Set[0,1], Set[2], @b1, @b3, @b3).should_not be_final Set[Arch[5,1]]
99
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b2, @b3, @b3).should_not be_final Set[Arch[5,1]]
100
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b1, @b3, @b4).should_not be_final Set[Arch[5,1]]
101
+ end
102
+
103
+ it 'should properly report G cell count (if G fits the provided Archs)' do
104
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b, @b2, @b2).g_cells(Set[Arch[5,1]]).should == 2
105
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b, @b2, @b2).g_cells(Set[Arch[4,2]]).should == 1
106
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b, @b2, @b2).g_cells(Set[Arch[3,2]]).should be_nil
107
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b, @b2, @b3).g_cells(Set[Arch[4,2]]).should == 2
108
+ end
109
+
110
+ it 'should properly report H cell count (if G fits the provided Archs)' do
111
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b1, @b1, @b2).h_cells(Set[Arch[5,1]]).should == 4
112
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b1, @b1, @b2).h_cells(Set[Arch[4,2]]).should == 2
113
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b1, @b1, @b2).h_cells(Set[Arch[3,1]]).should be_nil
114
+ Decomposition.new(@fsm, Set[0], Set[1,2], @b1, @b2, @b2).h_cells(Set[Arch[4,2]]).should == 3
115
+ end
116
+
117
+ end
118
+
119
+ end end
@@ -0,0 +1,161 @@
1
+ # encoding: UTF-8
2
+
3
+ module ArtDecomp describe Executable do
4
+
5
+ before do
6
+ @orig_stderr = $stderr
7
+ $stderr = StringIO.new
8
+ @fsm = 'spec/fixtures/fsm'
9
+ @dir = "#{Dir.tmpdir}/#{rand.to_s}"
10
+ @args = ['--archs', '5/1', '--outdir', @dir, @fsm]
11
+ end
12
+
13
+ after do
14
+ $stderr = @orig_stderr
15
+ FileUtils.rmtree @dir if Dir.exists? @dir
16
+ end
17
+
18
+ def stderr
19
+ $stderr.rewind
20
+ $stderr.read
21
+ end
22
+
23
+ it 'should require an FSM' do
24
+ lambda { Executable.new([]) }.should raise_error SystemExit
25
+ stderr.should =~ rex('no FSM given')
26
+ end
27
+
28
+ it 'should require that the FSM exists' do
29
+ lambda { Executable.new(['bogus']) }.should raise_error SystemExit
30
+ stderr.should =~ rex('FSM does not exist')
31
+ end
32
+
33
+ it 'should require at least one target Arch' do
34
+ lambda { Executable.new([@fsm]) }.should raise_error SystemExit
35
+ stderr.should =~ rex('no architecture given')
36
+ end
37
+
38
+ it 'should require that all architectures are parsable' do
39
+ args = ['--archs', '5/1', 'a/b', '--outdir', @dir, @fsm]
40
+ lambda { Executable.new(args) }.should raise_error SystemExit
41
+ stderr.should =~ rex('archs not in the form of inputs/outputs')
42
+ end
43
+
44
+ it 'should require output directory' do
45
+ lambda { Executable.new(['--archs', '5/1', '--', @fsm]) }.should raise_error SystemExit
46
+ stderr.should =~ rex('no output directory given')
47
+ end
48
+
49
+ it 'should require that the output directory does not exist' do
50
+ lambda { Executable.new(['--archs', '5/1', '--outdir', Dir.tmpdir, @fsm]) }.should raise_error SystemExit
51
+ stderr.should =~ rex('output directory exists')
52
+ end
53
+
54
+ it 'should require that the output directory is creatable' do
55
+ Dir.mkdir @dir, 0400
56
+ subdir = "#{@dir}/#{rand.to_s}"
57
+ lambda { Executable.new(['--archs', '5/1', '--outdir', subdir, @fsm]) }.should raise_error SystemExit
58
+ stderr.should =~ rex('output directory cannot be created')
59
+ end
60
+
61
+ it 'should create the output directory' do
62
+ Dir.exists?(@dir).should be_false
63
+ Executable.new @args
64
+ Dir.exists?(@dir).should be_true
65
+ end
66
+
67
+ it 'should validate that the specified UV generator exists' do
68
+ lambda { Executable.new(@args + ['--uv', 'bogus']) }.should raise_error SystemExit
69
+ stderr.should =~ rex('no such UV generator')
70
+ end
71
+
72
+ it 'should validate that the specified Qu generator exists' do
73
+ lambda { Executable.new(@args + ['--qu', 'bogus']) }.should raise_error SystemExit
74
+ stderr.should =~ rex('no such Qu generator')
75
+ end
76
+
77
+ it 'should validate that the specified Qv generator exists' do
78
+ lambda { Executable.new(@args + ['--qv', 'bogus']) }.should raise_error SystemExit
79
+ stderr.should =~ rex('no such Qv generator')
80
+ end
81
+
82
+ it 'should dump the resulting decompositions into a file' do
83
+ fsm = FSM.from_kiss 'spec/fixtures/fsm'
84
+ dec = Decomposition.new fsm, Set[0], Set[1], Blanket[B[0],B[1],B[2]], Blanket[], Blanket[]
85
+
86
+ decomposer = mock Decomposer, :decompositions => [dec, dec].each
87
+ Decomposer.should_receive(:new).with(:fsm => fsm, :archs => an_instance_of(Set), :uv_gens => [UVGenerator::Relevance], :qu_gens => [QuGenerator::EdgeLabels], :qv_gens => [QvGenerator::GraphColouring]).and_return decomposer
88
+
89
+ Executable.new(@args).run false
90
+ Marshal.load(File.read("#{@dir}/decompositions")).should == [dec, dec]
91
+ end
92
+
93
+ it 'should create files holding the resulting Decomposition objects and keep track of the best decomposition' do
94
+ dec0 = Decomposition.new FSM.from_kiss('spec/fixtures/fsm'), Set[0], Set[1], Blanket[B[0],B[1],B[2]], Blanket[], Blanket[]
95
+ dec1 = Decomposition.new FSM.from_kiss('spec/fixtures/fsm'), Set[1], Set[0], Blanket[B[0],B[1],B[2]], Blanket[], Blanket[]
96
+ Decomposer.should_receive(:new).and_return mock(Decomposer, :decompositions => [dec0, dec1].each)
97
+ ex = Executable.new @args
98
+ ex.best.should be_nil
99
+ ex.run
100
+ ex.best.should == 4
101
+ Marshal.load(File.read("#{@dir}/0.dec")).should == dec0
102
+ Marshal.load(File.read("#{@dir}/1.dec")).should == dec1
103
+ end
104
+
105
+ it 'should pass all of the requested generators and architectures to the Decomposer, and report on what it’s using when asked' do
106
+ fsm = mock FSM, :fsm_cells => nil, :implementable_in? => false, :stats => ''
107
+ FSM.should_receive(:from_kiss).with(@fsm).and_return fsm
108
+
109
+ decomposer = mock Decomposer, :decompositions => [].each
110
+ Decomposer.should_receive(:new).with(:fsm => fsm, :archs => Set[Arch[4,2], Arch[5,1]], :uv_gens => [UVGenerator::Braindead, UVGenerator::Braindead], :qu_gens => [QuGenerator::BlockTable, QuGenerator::EdgeLabels], :qv_gens => [QvGenerator::GraphMerging, QvGenerator::Bipainting]).and_return decomposer
111
+
112
+ args = ['--archs', '5/1', '4/2', '--uv', 'Braindead', 'Braindead', '--qu', 'BlockTable', 'EdgeLabels', '--qv', 'GraphMerging', 'Bipainting', '--outdir', @dir, @fsm]
113
+ ex = Executable.new args
114
+ ex.gens.should == 'Braindead+Braindead, BlockTable+EdgeLabels, GraphMerging+Bipainting'
115
+ ex.run
116
+ end
117
+
118
+ it 'should allow setting any of the generators to ‘all’' do
119
+ fsm = mock FSM, :fsm_cells => nil, :implementable_in? => false, :stats => ''
120
+ FSM.should_receive(:from_kiss).with(@fsm).and_return fsm
121
+
122
+ decomposer = mock Decomposer, :decompositions => [].each
123
+ Decomposer.should_receive(:new).with(:fsm => fsm, :archs => Set[Arch[5,1]], :uv_gens => [UVGenerator::Braindead, UVGenerator::Relevance], :qu_gens => [QuGenerator::BlockTable, QuGenerator::EdgeLabels], :qv_gens => [QvGenerator::Bipainting, QvGenerator::GraphColouring,QvGenerator::GraphMerging]).and_return decomposer
124
+
125
+ args = ['--archs', '5/1', '--uv', 'all', '--qu', 'all', '--qv', 'all', '--outdir', @dir, @fsm]
126
+ Executable.new(args).run
127
+ end
128
+
129
+ it 'should decompose iteratively according to the number of iterations and expose this number (along with archs and dir)' do
130
+ args = ['--archs', '2/1', '--iters', '2', '--outdir', @dir, 'spec/fixtures/lion']
131
+ dec = Decomposition.new FSM.from_kiss('spec/fixtures/lion'), Set[0], Set[1], Blanket[B[0],B[1],B[2]], Blanket[], Blanket[]
132
+ Decomposer.stub!(:new).and_return mock(Decomposer, :decompositions => [dec].each)
133
+ ex = Executable.new args
134
+ ex.archs.should == Set[Arch[2,1]]
135
+ ex.iters.should == 2
136
+ ex.dir.should == @dir
137
+ ex.run
138
+ File.exists?("#{@dir}/0.dec").should be_true
139
+ File.exists?("#{@dir}/0/0.dec").should be_true
140
+ File.exists?("#{@dir}/0/0/0.dec").should be_false
141
+ end
142
+
143
+ it 'should allow logging to the specified file/stream' do
144
+ log = Tempfile.new rand
145
+ Decomposer.should_receive(:new).and_return mock(Decomposer, :decompositions => [].each)
146
+ Executable.new(['--archs', '5/1', '4/2', '--debug', '--log', log.path, '--outdir', @dir, @fsm]).run
147
+ Logging.level.should == Logger::DEBUG
148
+ Logging.off
149
+ File.read(log.path).should =~ rex('FSM 4/2+10s → 5/1+4/2')
150
+ end
151
+
152
+ it 'should handle the s8 edge case with grace' do
153
+ log = Tempfile.new rand
154
+ Executable.new(['--archs', '2/1', '--log', log.path, '--outdir', @dir, 'spec/fixtures/s8']).run
155
+ Logging.off
156
+ File.read(log.path).should =~ rex('final best decomposition: 0 cells')
157
+ end
158
+
159
+ # FIXME: add specs for --non-disjoint and --deep-ndj
160
+
161
+ end end
@@ -0,0 +1,146 @@
1
+ # encoding: UTF-8
2
+
3
+ module ArtDecomp describe FSM do
4
+
5
+ context 'parsed from an example KISS file' do
6
+
7
+ before do
8
+ @fsm = FSM.from_kiss 'spec/fixtures/fsm'
9
+ @lion = FSM.from_kiss 'spec/fixtures/lion'
10
+ @mark1 = FSM.from_kiss 'spec/fixtures/mark1'
11
+ @mc = FSM.from_kiss 'spec/fixtures/mc'
12
+ @opus = FSM.from_kiss 'spec/fixtures/opus'
13
+ @s8 = FSM.from_kiss 'spec/fixtures/s8'
14
+ @s420 = FSM.from_kiss 'spec/fixtures/s420'
15
+ end
16
+
17
+ it 'should parse both KISS files and strings' do
18
+ @mc.should == FSM.from_kiss(File.read 'spec/fixtures/mc')
19
+ end
20
+
21
+ it 'should handle edge cases, like KISS files with arbitrary next-states' do
22
+ lambda { FSM.from_kiss 'spec/fixtures/ex5' }.should_not raise_error
23
+ end
24
+
25
+ it 'should properly report the number of inputs' do
26
+ @opus.input_count.should == 5
27
+ @lion.input_count.should == 2
28
+ @mc.input_count.should == 3
29
+ end
30
+
31
+ it 'should properly report the number of outputs' do
32
+ @opus.output_count.should == 6
33
+ @lion.output_count.should == 1
34
+ @mc.output_count.should == 5
35
+ end
36
+
37
+ it 'should properly generate the Q Blanket' do
38
+ @opus.beta_q.should == Blanket[B[0,1,2], B[0,3,4], B[0,5], B[0,6,7], B[0,8,9,10,11,12,13,14], B[0,15,16], B[0,17,18], B[0,19], B[0,20], B[0,21]]
39
+ @lion.beta_q.should == Blanket[B[0,1,2], B[3,4,5], B[6,7,8], B[9,10]]
40
+ @mc.beta_q.should == Blanket[B[0,1,2], B[3,4], B[5,6,7], B[8,9]]
41
+ end
42
+
43
+ it 'should properly generate the F Blanket' do
44
+ @opus.beta_f.should == Blanket[B[0,1], B[2,3,9], B[4,14], B[5,6], B[7,8,20,21], B[10,16], B[11,18], B[12,15], B[13,17], B[19]]
45
+ @lion.beta_f.should == Blanket[B[0,1,4], B[2], B[2,3,7], B[5,6,10], B[8,9]]
46
+ @mc.beta_f.should == Blanket[B[0,1], B[2], B[3], B[4], B[5], B[6,7], B[8], B[9]]
47
+ end
48
+
49
+ it 'should properly generate selected inputs Blankets' do
50
+ @mc.beta_x(Set[0]).should == Blanket[B[0,1,3,4,6,7,8,9], B[1,2,3,4,5,7,8,9]]
51
+ @mc.beta_x(Set[1,2]).should == Blanket[B[0,1,3,5,6,8], B[0,1,4,5,6,9], B[0,2,3,6,7,8], B[0,2,4,6,7,9]]
52
+ @mc.beta_x(Set[]).should == Blanket[B[0,1,2,3,4,5,6,7,8,9]]
53
+ end
54
+
55
+ it 'should properly generate its KISS representation' do
56
+ @opus.to_kiss.should == File.read('spec/fixtures/opus.to_kiss')
57
+ @lion.to_kiss.should == File.read('spec/fixtures/lion.to_kiss')
58
+ @mc.to_kiss.should == File.read('spec/fixtures/mc.to_kiss')
59
+ end
60
+
61
+ it 'should return given inputs’ encoding for the given row(s)' do
62
+ @opus.x_encoding(Set[2], B[0]).should == '1'
63
+ @opus.x_encoding(Set[0], B[0]).should == '-'
64
+ @opus.x_encoding(Set[0], B[7,8]).should == '0'
65
+ lambda { @opus.x_encoding Set[0], B[8,9] }.should raise_error(AmbiguousEncodingQuery, 'ambiguous encoding query: block 8,9')
66
+ @opus.x_encoding(Set[0,2], B[0]).should == '-1'
67
+ end
68
+
69
+ it 'should return output encoding for the given row(s)' do
70
+ @opus.y_encoding(B[0]).should == '110000'
71
+ @opus.y_encoding(B[0,1,2,3]).should == '110000'
72
+ lambda { @opus.y_encoding B[3,4] }.should raise_error(AmbiguousEncodingQuery, 'ambiguous encoding query: block 3,4')
73
+ @lion.y_encoding(B[2,3]).should == '1'
74
+ @mark1.y_encoding(B[0]).should == '-11---1-00------'
75
+ @mark1.y_encoding(B[0,16]).should == '-11---1-00100000'
76
+ end
77
+
78
+ it 'should return state encoding for the given row(s)' do
79
+ @opus.q_encoding(B[1]).should == 'init0'
80
+ @opus.q_encoding(B[1,2]).should == 'init0'
81
+ @opus.q_encoding(B[0,1,2]).should == 'init0'
82
+ @opus.q_encoding(B[0]).should == '-'
83
+ lambda { @opus.q_encoding B[2,3] }.should raise_error(AmbiguousEncodingQuery, 'ambiguous encoding query: block 2,3')
84
+ end
85
+
86
+ it 'should return the row(s) of a state matching next-state of given row(s)' do
87
+ @opus.state_rows_of_next_state_of(B[20,21]).should == B[0,8,9,10,11,12,13,14]
88
+ @mark1.state_rows_of_next_state_of(B[2]).should == B[0,22]
89
+ end
90
+
91
+ it 'should equal/not-equal other FSMs and hash properly' do
92
+ @lion.should_not == FSM.from_kiss('spec/fixtures/opus')
93
+ @lion.should == FSM.from_kiss('spec/fixtures/lion')
94
+ @lion.hash.should == FSM.from_kiss('spec/fixtures/lion').hash
95
+ @lion.should eql FSM.from_kiss('spec/fixtures/lion')
96
+ end
97
+
98
+ it 'should expand selected input columns and return a new FSM instance' do
99
+ @lion.expand_x(Set[0,1]).should == FSM.from_kiss('spec/fixtures/lion.exp')
100
+ @fsm.expand_x(Set[0,1,2,3]).should == FSM.from_kiss('spec/fixtures/fsm.exp')
101
+ @fsm.expand_x(Set[0,3]).should == FSM.from_kiss('spec/fixtures/fsm.partially-exp')
102
+ end
103
+
104
+ it 'should return self if asked to expand columns lacking don’t-cares' do
105
+ opus = FSM.from_kiss 'spec/fixtures/opus.to_kiss'
106
+ opus.expand_x(Set[2]).should equal opus
107
+ end
108
+
109
+ it 'should report its input/output/state counts' do
110
+ @fsm.stats.should == '4/2+10s'
111
+ @lion.stats.should == '2/1+4s'
112
+ @mark1.stats.should == '5/16+15s'
113
+ @mc.stats.should == '3/5+4s'
114
+ @opus.stats.should == '5/6+10s'
115
+ end
116
+
117
+ it 'should report whether its directly implementable in a given Set of Archs' do
118
+ @lion.should be_implementable_in Set[Arch[4,2]]
119
+ @mc.should_not be_implementable_in Set[Arch[4,2]]
120
+ @mc.should be_implementable_in Set[Arch[5,1]]
121
+ @s8.should be_implementable_in Set[Arch[2,1]]
122
+ end
123
+
124
+ it 'should report the cell count for a given Set of Archs (if it’s implementable in it)' do
125
+ @lion.fsm_cells(Set[Arch[4,1]]).should == 3
126
+ @lion.fsm_cells(Set[Arch[4,2]]).should == 2
127
+ @mc.fsm_cells(Set[Arch[4,2]]).should be_nil
128
+ @mc.fsm_cells(Set[Arch[5,1]]).should == 7
129
+ @s8.fsm_cells(Set[Arch[2,1]]).should == 0
130
+ @s8.fsm_cells(Set[Arch[3,2]]).should == 0
131
+ @s8.fsm_cells(Set[Arch[4,1]]).should == 0
132
+ end
133
+
134
+ it 'should report its input relevance, and drop irrelevant inputs' do
135
+ @fsm.input_relevance.should == [2, 1, 3, 0, nil, nil, nil, nil]
136
+ @lion.input_relevance.should == [0, nil, nil, 1]
137
+ @mark1.input_relevance.should == [nil, nil, nil, nil, 0, 3, 2, 4, 1]
138
+ @mc.input_relevance.should == [nil, nil, 2, 1, 0]
139
+ @opus.input_relevance.should == [nil, nil, nil, nil, 2, 3, 4, 0, 1]
140
+ @s8.input_relevance.should == [3, 2, 1, 0, nil, nil, nil]
141
+ @s420.input_relevance.should == [1, 0, 18, nil, nil, nil, nil, nil, 17, 16, 15, 14, 13]
142
+ end
143
+
144
+ end
145
+
146
+ end end