art-decomp 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec/rake/spectask'
4
+
5
+ Spec::Rake::SpecTask.new :cov do |t|
6
+ t.rcov = true
7
+ t.rcov_opts = ['--exclude', 'spec']
8
+ t.spec_opts = ['--options', 'spec/spec.opts']
9
+ end
10
+
11
+ Spec::Rake::SpecTask.new :spec do |t|
12
+ t.spec_opts = ['--options', 'spec/spec.opts']
13
+ end
14
+
15
+ namespace :spec do
16
+ FileList['spec/**/*_spec.rb'].each do |file|
17
+ spec = file.split('/').last[0...-8]
18
+ desc "Run the #{spec} spec"
19
+ Spec::Rake::SpecTask.new spec do |t|
20
+ t.spec_files = [file]
21
+ t.spec_opts = ['--options', 'spec/spec.opts']
22
+ end
23
+ end
24
+ end
25
+
26
+ require 'jeweler'
27
+
28
+ Jeweler::Tasks.new do |gem|
29
+ gem.authors = ['Piotr Szotkowski']
30
+ gem.email = 'p.szotkowski@tele.pw.edu.pl'
31
+ gem.homepage = 'http://github.com/chastell/art-decomp'
32
+ gem.name = 'art-decomp'
33
+ gem.summary = 'art décomp: an FSM → FPGA decomposer'
34
+
35
+ gem.files.exclude 'kiss/**/*'
36
+
37
+ gem.add_dependency 'teejayvanslyke-gazer'
38
+ gem.add_dependency 'trollop'
39
+
40
+ gem.add_development_dependency 'diff-lcs'
41
+ gem.add_development_dependency 'jeweler'
42
+ gem.add_development_dependency 'rcov'
43
+ gem.add_development_dependency 'rspec'
44
+ end
45
+
46
+ Jeweler::GemcutterTasks.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
data/bin/ad-compare ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require_relative '../lib/art-decomp'
5
+
6
+ input_limit = ARGV.first.to_i.zero? ? 0 : ARGV.shift.to_i
7
+
8
+ ARGV.each do |fsm_file|
9
+ fsm = ArtDecomp::FSM.from_kiss fsm_file
10
+ seps = fsm.beta_f.seps
11
+ input_seps = (0...fsm.input_count).map { |i| fsm.beta_x(Set[i]).seps & seps }
12
+
13
+ puts
14
+ puts fsm_file
15
+ puts "X seps: #{input_seps.map(&:size).inspect}"
16
+
17
+ (0...input_seps.size).pairs.to_a.each do |a, b|
18
+ puts "#{a} ⊂ #{b}" if input_seps[a].proper_subset? input_seps[b]
19
+ puts "#{a} = #{b}" if input_seps[a] == input_seps[b]
20
+ puts "#{a} ⊃ #{b}" if input_seps[a].proper_superset? input_seps[b]
21
+ end
22
+ end
data/bin/ad-console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems' if RUBY_VERSION < '1.9'
4
+ require 'backports/1.9' if RUBY_VERSION < '1.9'
5
+
6
+ require 'irb'
7
+
8
+ require_relative '../lib/art-decomp'
9
+ include ArtDecomp
10
+
11
+ IRB.start
data/bin/ad-inputs ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require 'rubygems' if RUBY_VERSION < '1.9'
5
+ require 'backports/1.9' if RUBY_VERSION < '1.9'
6
+
7
+ require_relative '../lib/art-decomp'
8
+
9
+ input_limit = ARGV.first.to_i.zero? ? 0 : ARGV.shift.to_i
10
+
11
+ ARGV.each do |fsm_file|
12
+ fsm = ArtDecomp::FSM.from_kiss fsm_file
13
+ next if fsm.input_count < input_limit
14
+ seps = fsm.beta_f.seps
15
+ rel_q = (fsm.beta_q.seps & seps).size
16
+ puts
17
+ puts fsm_file
18
+ puts "inputs: #{fsm.input_count}"
19
+ puts "βf sep: #{seps.size}"
20
+ puts "βq sep: #{rel_q}"
21
+ puts "βq.pin: #{fsm.beta_q.pins}"
22
+ puts "perpin: #{rel_q.to_f / fsm.beta_q.pins}"
23
+ puts "X seps: #{(0...fsm.input_count).map { |i| (fsm.beta_x(Set[i]).seps & seps).size }.sort.reverse.inspect}"
24
+ end
data/bin/ad-validate ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems' if RUBY_VERSION < '1.9'
4
+ require 'backports/1.9' if RUBY_VERSION < '1.9'
5
+
6
+ require_relative '../lib/art-decomp'
7
+
8
+ $stdout.sync = true
9
+ p Marshal.load(File.open ARGV.first).all? { |d| print '.'; d.valid? }
data/bin/art-decomp ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems' if RUBY_VERSION < '1.9'
4
+ require 'backports/1.9' if RUBY_VERSION < '1.9'
5
+
6
+ require_relative '../lib/art-decomp'
7
+
8
+ ArtDecomp::Executable.new.run
@@ -0,0 +1,32 @@
1
+ module ArtDecomp class Arch
2
+
3
+ attr_reader :pins, :pons
4
+
5
+ def self.[] pins, pons
6
+ new pins, pons
7
+ end
8
+
9
+ def initialize pins, pons
10
+ @pins, @pons = pins, pons
11
+ end
12
+
13
+ def == other
14
+ [@pins, @pons] == [other.pins, other.pons]
15
+ end
16
+
17
+ def cells archs
18
+ pons = archs.select { |a| a.pins >= @pins }.map(&:pons).max
19
+ (@pons % pons).zero? ? @pons / pons : @pons / pons + 1 rescue nil
20
+ end
21
+
22
+ alias eql? ==
23
+
24
+ def hash
25
+ @pins.hash ^ @pons.hash
26
+ end
27
+
28
+ def to_s
29
+ "#{pins}/#{pons}"
30
+ end
31
+
32
+ end end
@@ -0,0 +1,7 @@
1
+ module ArtDecomp class B
2
+
3
+ def self.[] *ints
4
+ ints.inject(0) { |int, elem| int | 1 << elem }
5
+ end
6
+
7
+ end end
@@ -0,0 +1,142 @@
1
+ module ArtDecomp class Bipainter
2
+
3
+ def initialize beta_q, beta_v, seps
4
+ raise 'non-disjoint beta_v' if beta_v.ints.pairs.any? { |a, b| a & b != 0 }
5
+ @beta_v = beta_v
6
+ @qv_colours = {}
7
+ @g_colours = {}
8
+ @qv_forbidden = Hash.new { |h, k| h[k] = Set[] }
9
+ @g_forbidden = Hash.new { |h, k| h[k] = Set[] }
10
+ @qv_graph = Graph.new beta_q, seps - beta_v.seps
11
+ @g_graph = Graph.new beta_q * beta_v, seps
12
+ end
13
+
14
+ def blankets
15
+ colour_next_vertex! until painted?
16
+ qv_blocks = Hash.new 0
17
+ g_blocks = Hash.new 0
18
+ @qv_colours.each { |vertex, colour| qv_blocks[colour] |= vertex }
19
+ @g_colours.each { |vertex, colour| g_blocks[colour] |= vertex }
20
+ [Blanket.new(qv_blocks.values), Blanket.new(g_blocks.values)]
21
+ end
22
+
23
+ private
24
+
25
+ def backup!
26
+ @g_forbidden.default = nil
27
+ @qv_forbidden.default = nil
28
+ @backup = {
29
+ :g_colours => Marshal.dump(@g_colours),
30
+ :g_forbidden => Marshal.dump(@g_forbidden),
31
+ :qv_colours => Marshal.dump(@qv_colours),
32
+ :qv_forbidden => Marshal.dump(@qv_forbidden),
33
+ }
34
+ @g_forbidden.default_proc = proc { |h, k| h[k] = Set[] }
35
+ @qv_forbidden.default_proc = proc { |h, k| h[k] = Set[] }
36
+ end
37
+
38
+ def restore!
39
+ @g_colours = Marshal.load @backup[:g_colours]
40
+ @g_forbidden = Marshal.load @backup[:g_forbidden]
41
+ @qv_colours = Marshal.load @backup[:qv_colours]
42
+ @qv_forbidden = Marshal.load @backup[:qv_forbidden]
43
+ @g_forbidden.default_proc = proc { |h, k| h[k] = Set[] }
44
+ @qv_forbidden.default_proc = proc { |h, k| h[k] = Set[] }
45
+ end
46
+
47
+ def colour_g_vertex! g_vertex
48
+ begin
49
+ backup!
50
+ colour = :a
51
+ colour = colour.next while @g_forbidden[g_vertex].include? colour
52
+ colour_g! g_vertex, colour
53
+ rescue PaintingError
54
+ restore!
55
+ forbid_g! g_vertex, colour
56
+ retry
57
+ end
58
+ end
59
+
60
+ def colour_next_vertex!
61
+ # FIXME: consider colouring G graph’s vertex first
62
+ # FIXME: consider other vertex selection algorithms
63
+ qv_vertex = (@qv_graph.vertices - @qv_colours.keys).sort_by { |v| [-@qv_forbidden[v].size, -@qv_graph.degree(v)] }.first
64
+ colour_qv_vertex! qv_vertex if qv_vertex
65
+ g_vertex = (@g_graph.vertices - @g_colours.keys).sort_by { |v| [-@g_forbidden[v].size, -@g_graph.degree(v)] }.first
66
+ colour_g_vertex! g_vertex if g_vertex
67
+ end
68
+
69
+ def colour_qv_vertex! qv_vertex
70
+ begin
71
+ backup!
72
+ colour = :a
73
+ colour = colour.next while @qv_forbidden[qv_vertex].include? colour
74
+ colour_qv! qv_vertex, colour
75
+ rescue PaintingError
76
+ restore!
77
+ forbid_qv! qv_vertex, colour
78
+ retry
79
+ end
80
+ end
81
+
82
+ def forbid_g! g_vertex, colour
83
+ return if @g_forbidden[g_vertex].include? colour
84
+ raise PaintingError if colour == @g_colours[g_vertex]
85
+ @g_forbidden[g_vertex] << colour
86
+ siblings_of(g_vertex).each { |sibling| forbid_g! sibling, colour }
87
+ end
88
+
89
+ def forbid_qv! qv_vertex, colour
90
+ return if @qv_forbidden[qv_vertex].include? colour
91
+ raise PaintingError if colour == @qv_colours[qv_vertex]
92
+ @qv_forbidden[qv_vertex] << colour
93
+ end
94
+
95
+ def colour_g! g_vertex, colour
96
+ return if @g_colours[g_vertex] == colour
97
+ raise PaintingError if @g_colours[g_vertex] and @g_colours[g_vertex] != colour
98
+ raise PaintingError if @g_forbidden[g_vertex].include? colour
99
+ @g_colours[g_vertex] = colour
100
+ @g_graph.adjacent(g_vertex).each { |adjacent| forbid_g! adjacent, colour }
101
+ siblings_of(g_vertex).each { |sibling| colour_g! sibling, colour }
102
+ end
103
+
104
+ def colour_qv! qv_vertex, colour
105
+ return if @qv_colours[qv_vertex] == colour
106
+ raise PaintingError if @qv_colours[qv_vertex] and @qv_colours[qv_vertex] != colour
107
+ raise PaintingError if @qv_forbidden[qv_vertex].include? colour
108
+ @qv_colours[qv_vertex] = colour
109
+ @qv_graph.adjacent(qv_vertex).each { |adjacent| forbid_qv! adjacent, colour }
110
+ if @qv_colours.any? { |q, col| q != qv_vertex and col == colour }
111
+ @g_graph.vertices.select { |g| g & qv_vertex == g }.each do |g_vertex|
112
+ v_parent = @beta_v.ints.find { |v| v & g_vertex == g_vertex }
113
+ @g_graph.adjacent(g_vertex).select { |g| v_parent & g == g and qv_vertex & g != g }.each do |neighbour|
114
+ @qv_graph.vertices.select { |q| q & neighbour == neighbour }.each do |q_parent|
115
+ forbid_qv! q_parent, colour
116
+ end
117
+ end
118
+ siblings_of(g_vertex).each { |sibling| sync_colours g_vertex, sibling }
119
+ end
120
+ end
121
+ end
122
+
123
+ def siblings_of g_vertex
124
+ v_parent = @beta_v.ints.find { |v| v & g_vertex == g_vertex }
125
+ colours = @qv_colours.select { |q, col| g_vertex & q == g_vertex }.values
126
+ similar = @qv_colours.select { |q, col| colours.include? col }.keys
127
+ (similar.map { |q| q & v_parent }.to_set & @g_graph.vertices).delete g_vertex
128
+ end
129
+
130
+ def sync_colours v1, v2
131
+ (@g_forbidden[v1] - @g_forbidden[v2]).each { |col| forbid_g! v2, col }
132
+ (@g_forbidden[v2] - @g_forbidden[v1]).each { |col| forbid_g! v1, col }
133
+ if @g_colours[v1] then colour_g! v2, @g_colours[v1]
134
+ elsif @g_colours[v2] then colour_g! v1, @g_colours[v2]
135
+ end
136
+ end
137
+
138
+ def painted?
139
+ @qv_graph.vertices == @qv_colours.keys.to_set and @g_graph.vertices == @g_colours.keys.to_set
140
+ end
141
+
142
+ end end
@@ -0,0 +1,82 @@
1
+ module ArtDecomp class Blanket
2
+
3
+ attr_reader :ints
4
+
5
+ def self.[] *ints
6
+ new ints
7
+ end
8
+
9
+ def self.from_array array
10
+ ints = Hash.new 0
11
+ array.each_with_index do |enc, i|
12
+ ints[enc] |= 1 << i
13
+ end
14
+ ints.each_key do |key|
15
+ ints[key] |= ints[DontCare]
16
+ end unless ints[DontCare].zero?
17
+ ints.delete DontCare unless ints.size <= 1
18
+ new ints.values
19
+ end
20
+
21
+ def initialize ints
22
+ @ints = ints.to_set.delete(0).freeze
23
+ end
24
+
25
+ def * other
26
+ ints = []
27
+ @ints.each do |this_int|
28
+ other.ints.each do |other_int|
29
+ ints << (this_int & other_int)
30
+ end
31
+ end
32
+ Blanket.new ints
33
+ end
34
+
35
+ def == other
36
+ @ints == other.ints
37
+ end
38
+
39
+ def encoding bits
40
+ encs = encodings bits
41
+ encs.size == 1 ? encs.first : raise(AmbiguousEncodingQuery, "ambiguous encoding query: block #{bits.bits.join ','}")
42
+ end
43
+
44
+ def encodings bits
45
+ sorted = @ints.sort
46
+ width = sorted.size.log2_ceil
47
+ encs = @ints.select { |int| int & bits == bits }.map { |int| sorted.index(int) }.map { |i| i.to_s(2).rjust width, '0' }
48
+ encs.size == 0 or encs.size == @ints.size ? [DontCare.to_s * width] : encs
49
+ end
50
+
51
+ alias eql? ==
52
+
53
+ def hash
54
+ @ints.hash
55
+ end
56
+
57
+ def inspect
58
+ blocks = @ints.map(&:bits).sort.map { |bl| "B[#{bl.join ','}]" }
59
+ "Blanket[#{blocks.join ', '}]"
60
+ end
61
+
62
+ def pins
63
+ @ints.size.log2_ceil
64
+ end
65
+
66
+ def seps
67
+ # FIXME: consider an algorithm with lesser complexity
68
+ seps = Set[]
69
+ singles = 0
70
+ @ints.pairs.each { |int1, int2| singles |= int1 ^ int2 }
71
+ singles.bits.pairs.each do |elem1, elem2|
72
+ sep = Sep[elem1, elem2]
73
+ seps << sep unless @ints.any? { |int| int & sep == sep }
74
+ end
75
+ seps
76
+ end
77
+
78
+ def size
79
+ @ints.size
80
+ end
81
+
82
+ end end
@@ -0,0 +1,75 @@
1
+ module ArtDecomp class Decomposer
2
+
3
+ def initialize params
4
+ @archs = params[:archs]
5
+ @uv_gens = params[:uv_gens].map { |gen| gen.new params[:fsm], params[:archs] }
6
+ @qu_gens = params[:qu_gens].map &:new
7
+ @qv_gens = params[:qv_gens].map &:new
8
+ end
9
+
10
+ def decompositions opts = {}
11
+ @seen = Set[]
12
+ Enumerator.new do |yielder|
13
+ @uv_gens.each do |uv_gen|
14
+ uv_gen.uv_pairs.each do |fsm, u, v|
15
+ unless @seen.include? [fsm, u, v]
16
+ @qu_gens.each do |qu_gen|
17
+ qu_gen.blankets(fsm, u, v).each do |qu|
18
+ unless @seen.include? [fsm, u, v, qu]
19
+ @qv_gens.each do |qv_gen|
20
+ qv_gen.blankets(fsm, u, v, qu).each do |qv, g|
21
+ unless @seen.include? [fsm, u, v, qu, qv, g]
22
+ dec = Decomposition.new fsm, u, v, qu, qv, g
23
+ if dec.sensible? @archs
24
+ if opts[:non_disjoint]
25
+ non_disjoint(fsm, u, v, qu, qv, g, opts).each do |ndj|
26
+ yielder.yield ndj
27
+ end
28
+ end
29
+ yielder.yield dec
30
+ end
31
+ @seen << [fsm, u, v, qu, qv, g] unless opts[:keep_seen]
32
+ end
33
+ end
34
+ end
35
+ @seen << [fsm, u, v, qu] unless opts[:keep_seen]
36
+ end
37
+ end
38
+ end
39
+ @seen << [fsm, u, v] unless opts[:keep_seen]
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def non_disjoint fsm, u_dj, v, qu, qv_dj, g_dj, opts
49
+ Enumerator.new do |yielder|
50
+ (v - u_dj).each do |v_input|
51
+ u = u_dj + [v_input]
52
+ unless @seen.include? [fsm, u, v, qu]
53
+ @qv_gens.each do |qv_gen|
54
+ qv_gen.blankets(fsm, u, v, qu).each do |qv, g|
55
+ unless @seen.include? [fsm, u, v, qu, qv, g]
56
+ dec = Decomposition.new fsm, u, v, qu, qv, g
57
+ if dec.sensible? @archs and g.pins < g_dj.pins
58
+ if opts[:deep_ndj]
59
+ non_disjoint(fsm, u, v, qu, qv, g, opts).each do |ndj|
60
+ yielder.yield ndj
61
+ end
62
+ end
63
+ yielder.yield dec
64
+ end
65
+ @seen << [fsm, u, v, qu, qv, g] unless opts[:keep_seen]
66
+ end
67
+ end
68
+ end
69
+ @seen << [fsm, u, v, qu] unless opts[:keep_seen]
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ end end
@@ -0,0 +1,91 @@
1
+ module ArtDecomp class Decomposition
2
+
3
+ def initialize fsm, u, v, qu, qv, g, opts = {}
4
+ @fsm, @u, @v, @qu, @qv, @g = fsm, u, v, qu, qv, g
5
+ end
6
+
7
+ def == other
8
+ [@fsm, @u, @v, @qu, @qv, @g] == [other.fsm, other.u, other.v, other.qu, other.qv, other.g]
9
+ end
10
+
11
+ def decomposable?
12
+ @qu.size > 2
13
+ end
14
+
15
+ def disjoint?
16
+ (@u & @v).empty?
17
+ end
18
+
19
+ alias eql? ==
20
+
21
+ def f_kiss
22
+ @fsm.to_kiss
23
+ end
24
+
25
+ def final? archs
26
+ pins = archs.map(&:pins).max
27
+ @v.size + @qv.pins <= pins and @u.size + @qu.pins + @g.pins <= pins
28
+ end
29
+
30
+ def g_cells archs
31
+ Arch[@v.size + @qv.pins, @g.pins].cells archs
32
+ end
33
+
34
+ def g_kiss
35
+ lines = (@fsm.beta_x(@v) * @qv).ints.map do |row|
36
+ v = @fsm.x_encoding @v, row
37
+ qv = @qv.encoding row
38
+ g = @g.encoding row
39
+ "#{v}#{qv} #{g}"
40
+ end
41
+ KISS.new(lines).formatted
42
+ end
43
+
44
+ def h_cells archs
45
+ Arch[@u.size + @qu.pins + @g.pins, @fsm.output_count + @qu.pins + @qv.pins].cells archs
46
+ end
47
+
48
+ def h_kiss
49
+ lines = (@fsm.beta_x(@u) * @g * @qu).ints.map do |row|
50
+ u = @fsm.x_encoding @u, row
51
+ qu = @qu.encoding row
52
+ qup = @qu.encoding @fsm.state_rows_of_next_state_of(row)
53
+ qvp = @qv.encoding @fsm.state_rows_of_next_state_of(row)
54
+ y = @fsm.y_encoding row
55
+ qu = '*' if qu =~ /^-+$/
56
+ qup = '*' if qup =~ /^-+$/
57
+ # FIXME: use only the encoding(s) really mapped from this row
58
+ @g.encodings(row).map do |g|
59
+ "#{u}#{g} #{qu} #{qup} #{qvp}#{y}"
60
+ end
61
+ end.flatten
62
+ KISS.new(lines).formatted
63
+ end
64
+
65
+ def hash
66
+ @fsm.hash ^ @u.hash ^ @v.hash ^ @qu.hash ^ @qv.hash ^ @g.hash
67
+ end
68
+
69
+ def q_kiss
70
+ lines = @fsm.beta_q.ints.map do |row|
71
+ qu = @qu.encoding row
72
+ qv = @qv.encoding row
73
+ q = @fsm.q_encoding row
74
+ "#{qu} #{qv} #{q}"
75
+ end
76
+ KISS.new(lines).formatted
77
+ end
78
+
79
+ def sensible? archs
80
+ @v.size + @qv.pins <= archs.map(&:pins).max and @u.size + @qu.pins + @g.pins < @fsm.input_count + @fsm.beta_q.pins
81
+ end
82
+
83
+ def valid?
84
+ @g.seps.subset?((@fsm.beta_x(@v) * @qv).seps) and @fsm.beta_f.seps.subset?((@fsm.beta_x(@u) * @qu * @g).seps)
85
+ end
86
+
87
+ protected
88
+
89
+ attr_reader :fsm, :u, :v, :qu, :qv, :g
90
+
91
+ end end
@@ -0,0 +1,9 @@
1
+ module ArtDecomp
2
+
3
+ class AmbiguousEncodingQuery < RuntimeError
4
+ end
5
+
6
+ class PaintingError < RuntimeError
7
+ end
8
+
9
+ end
@@ -0,0 +1,94 @@
1
+ require 'trollop'
2
+
3
+ module ArtDecomp class Executable
4
+
5
+ attr_reader :archs, :best, :iters, :dir
6
+
7
+ def initialize args = ARGV
8
+ opts = Trollop.options(args) do
9
+ opt :archs, 'Target architecture(s)', :type => :strings
10
+ opt :outdir, 'Output directory', :type => :string
11
+ opt :iters, 'Number of iterations, 0 for infinite', :default => 1
12
+ opt :uv, 'UV generator(s)', :default => ['Relevance']
13
+ opt :qu, 'Qu generator(s)', :default => ['EdgeLabels']
14
+ opt :qv, 'Qv generator(s)', :default => ['GraphColouring']
15
+ opt :non_disjoint, 'Compute non-disjoint decompositions', :default => false
16
+ opt :deep_ndj, 'Compute deep non-dj decompositions', :default => false
17
+ opt :log, 'Logging target', :type => :string
18
+ opt :debug, 'Log debug-level activities', :default => false
19
+ end
20
+
21
+ opts[:uv] = UVGenerator.constants.map(&:to_s).sort if opts[:uv] == ['all']
22
+ opts[:qu] = QuGenerator.constants.map(&:to_s).sort if opts[:qu] == ['all']
23
+ opts[:qv] = QvGenerator.constants.map(&:to_s).sort if opts[:qv] == ['all']
24
+
25
+ Trollop.die 'no FSM given' if args.empty?
26
+ Trollop.die 'FSM does not exist' unless File.exists? args.first
27
+ Trollop.die 'no architecture given' unless opts[:archs_given]
28
+ Trollop.die 'no output directory given' unless opts[:outdir_given]
29
+ Trollop.die :archs, 'not in the form of inputs/outputs' unless opts[:archs].all? { |s| s =~ /^\d+\/\d+$/ }
30
+ Trollop.die :outdir, 'output directory exists' if File.exists? opts[:outdir]
31
+ Trollop.die :uv, 'no such UV generator' unless (opts[:uv] - UVGenerator.constants.map(&:to_s)).empty?
32
+ Trollop.die :qu, 'no such Qu generator' unless (opts[:qu] - QuGenerator.constants.map(&:to_s)).empty?
33
+ Trollop.die :qv, 'no such Qv generator' unless (opts[:qv] - QvGenerator.constants.map(&:to_s)).empty?
34
+
35
+ Dir.mkdir opts[:outdir] rescue Trollop.die :outdir, 'output directory cannot be created'
36
+
37
+ @dir = opts[:outdir]
38
+ @fsm = FSM.from_kiss args.first
39
+ @archs = opts[:archs].map { |s| Arch[*s.split('/').map(&:to_i)] }.to_set
40
+ @iters = opts[:iters]
41
+ @non_disjoint = opts[:non_disjoint]
42
+ @deep_ndj = opts[:deep_ndj]
43
+
44
+ @uv_gens = opts[:uv].map { |gen| eval "UVGenerator::#{gen}" }
45
+ @qu_gens = opts[:qu].map { |gen| eval "QuGenerator::#{gen}" }
46
+ @qv_gens = opts[:qv].map { |gen| eval "QvGenerator::#{gen}" }
47
+
48
+ if opts[:log_given]
49
+ require_relative 'logging'
50
+ Logging.log = opts[:log] == '-' ? $stdout : opts[:log]
51
+ Logging.level = Logger::DEBUG if opts[:debug]
52
+ end
53
+ end
54
+
55
+ def gens
56
+ [@uv_gens, @qu_gens, @qv_gens].map do |gens|
57
+ gens.map { |gen| gen.to_s.split('::').last }.join '+'
58
+ end.join ', '
59
+ end
60
+
61
+ def run dump_decs = true
62
+ @best = @fsm.fsm_cells @archs
63
+ dumps = Hash.new { |h, k| h[k] = [] }
64
+ decompositions(@fsm, @iters, @dir, 0).each do |dec, dir, i|
65
+ dumps[dir] << dec
66
+ File.dump_object dec, "#{dir}/#{i}.dec" if dump_decs
67
+ end unless @fsm.implementable_in? @archs
68
+ dumps.each do |dir, decs|
69
+ File.dump_object decs, "#{dir}/decompositions"
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def decompositions fsm, iters, dir, cells
76
+ decomposer = Decomposer.new :fsm => fsm, :archs => @archs, :uv_gens => @uv_gens, :qu_gens => @qu_gens, :qv_gens => @qv_gens
77
+ Enumerator.new do |yielder|
78
+ decomposer.decompositions(:non_disjoint => @non_disjoint, :deep_ndj => @deep_ndj).with_index do |dec, i|
79
+ yielder.yield dec, dir, i
80
+ if dec.final? @archs
81
+ this = cells + dec.g_cells(@archs) + dec.h_cells(@archs)
82
+ @best = this if @best.nil? or this < @best
83
+ elsif iters != 1 and dec.decomposable? and (@best.nil? or cells < @best)
84
+ in_dir = "#{dir}/#{i}"
85
+ Dir.mkdir in_dir
86
+ decompositions(FSM.from_kiss(dec.h_kiss), iters - 1, in_dir, cells + dec.g_cells(@archs)).each do |in_dec, in_dir, in_i|
87
+ yielder.yield in_dec, in_dir, in_i
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ end end