CooCoo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/CooCoo.gemspec +47 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +88 -0
  6. data/README.md +123 -0
  7. data/Rakefile +81 -0
  8. data/bin/cuda-dev-info +25 -0
  9. data/bin/cuda-free +28 -0
  10. data/bin/cuda-free-trend +7 -0
  11. data/bin/ffi-gen +267 -0
  12. data/bin/spec_runner_html.sh +42 -0
  13. data/bin/trainer +198 -0
  14. data/bin/trend-cost +13 -0
  15. data/examples/char-rnn.rb +405 -0
  16. data/examples/cifar/cifar.rb +94 -0
  17. data/examples/img-similarity.rb +201 -0
  18. data/examples/math_ops.rb +57 -0
  19. data/examples/mnist.rb +365 -0
  20. data/examples/mnist_classifier.rb +293 -0
  21. data/examples/mnist_dream.rb +214 -0
  22. data/examples/seeds.rb +268 -0
  23. data/examples/seeds_dataset.txt +210 -0
  24. data/examples/t10k-images-idx3-ubyte +0 -0
  25. data/examples/t10k-labels-idx1-ubyte +0 -0
  26. data/examples/train-images-idx3-ubyte +0 -0
  27. data/examples/train-labels-idx1-ubyte +0 -0
  28. data/ext/buffer/Rakefile +50 -0
  29. data/ext/buffer/buffer.pre.cu +727 -0
  30. data/ext/buffer/matrix.pre.cu +49 -0
  31. data/lib/CooCoo.rb +1 -0
  32. data/lib/coo-coo.rb +18 -0
  33. data/lib/coo-coo/activation_functions.rb +344 -0
  34. data/lib/coo-coo/consts.rb +5 -0
  35. data/lib/coo-coo/convolution.rb +298 -0
  36. data/lib/coo-coo/core_ext.rb +75 -0
  37. data/lib/coo-coo/cost_functions.rb +91 -0
  38. data/lib/coo-coo/cuda.rb +116 -0
  39. data/lib/coo-coo/cuda/device_buffer.rb +240 -0
  40. data/lib/coo-coo/cuda/device_buffer/ffi.rb +109 -0
  41. data/lib/coo-coo/cuda/error.rb +51 -0
  42. data/lib/coo-coo/cuda/host_buffer.rb +117 -0
  43. data/lib/coo-coo/cuda/runtime.rb +157 -0
  44. data/lib/coo-coo/cuda/vector.rb +315 -0
  45. data/lib/coo-coo/data_sources.rb +2 -0
  46. data/lib/coo-coo/data_sources/xournal.rb +25 -0
  47. data/lib/coo-coo/data_sources/xournal/bitmap_stream.rb +197 -0
  48. data/lib/coo-coo/data_sources/xournal/document.rb +377 -0
  49. data/lib/coo-coo/data_sources/xournal/loader.rb +144 -0
  50. data/lib/coo-coo/data_sources/xournal/renderer.rb +101 -0
  51. data/lib/coo-coo/data_sources/xournal/saver.rb +99 -0
  52. data/lib/coo-coo/data_sources/xournal/training_document.rb +78 -0
  53. data/lib/coo-coo/data_sources/xournal/training_document/constants.rb +15 -0
  54. data/lib/coo-coo/data_sources/xournal/training_document/document_maker.rb +89 -0
  55. data/lib/coo-coo/data_sources/xournal/training_document/document_reader.rb +105 -0
  56. data/lib/coo-coo/data_sources/xournal/training_document/example.rb +37 -0
  57. data/lib/coo-coo/data_sources/xournal/training_document/sets.rb +76 -0
  58. data/lib/coo-coo/debug.rb +8 -0
  59. data/lib/coo-coo/dot.rb +129 -0
  60. data/lib/coo-coo/drawing.rb +4 -0
  61. data/lib/coo-coo/drawing/cairo_canvas.rb +100 -0
  62. data/lib/coo-coo/drawing/canvas.rb +68 -0
  63. data/lib/coo-coo/drawing/chunky_canvas.rb +101 -0
  64. data/lib/coo-coo/drawing/sixel.rb +214 -0
  65. data/lib/coo-coo/enum.rb +17 -0
  66. data/lib/coo-coo/from_name.rb +58 -0
  67. data/lib/coo-coo/fully_connected_layer.rb +205 -0
  68. data/lib/coo-coo/generation_script.rb +38 -0
  69. data/lib/coo-coo/grapher.rb +140 -0
  70. data/lib/coo-coo/image.rb +286 -0
  71. data/lib/coo-coo/layer.rb +67 -0
  72. data/lib/coo-coo/layer_factory.rb +26 -0
  73. data/lib/coo-coo/linear_layer.rb +59 -0
  74. data/lib/coo-coo/math.rb +607 -0
  75. data/lib/coo-coo/math/abstract_vector.rb +121 -0
  76. data/lib/coo-coo/math/functions.rb +39 -0
  77. data/lib/coo-coo/math/interpolation.rb +7 -0
  78. data/lib/coo-coo/network.rb +264 -0
  79. data/lib/coo-coo/neuron.rb +112 -0
  80. data/lib/coo-coo/neuron_layer.rb +168 -0
  81. data/lib/coo-coo/option_parser.rb +18 -0
  82. data/lib/coo-coo/platform.rb +17 -0
  83. data/lib/coo-coo/progress_bar.rb +11 -0
  84. data/lib/coo-coo/recurrence/backend.rb +99 -0
  85. data/lib/coo-coo/recurrence/frontend.rb +101 -0
  86. data/lib/coo-coo/sequence.rb +187 -0
  87. data/lib/coo-coo/shell.rb +2 -0
  88. data/lib/coo-coo/temporal_network.rb +291 -0
  89. data/lib/coo-coo/trainer.rb +21 -0
  90. data/lib/coo-coo/trainer/base.rb +67 -0
  91. data/lib/coo-coo/trainer/batch.rb +82 -0
  92. data/lib/coo-coo/trainer/batch_stats.rb +27 -0
  93. data/lib/coo-coo/trainer/momentum_stochastic.rb +59 -0
  94. data/lib/coo-coo/trainer/stochastic.rb +47 -0
  95. data/lib/coo-coo/transformer.rb +272 -0
  96. data/lib/coo-coo/vector_layer.rb +194 -0
  97. data/lib/coo-coo/version.rb +3 -0
  98. data/lib/coo-coo/weight_deltas.rb +23 -0
  99. data/prototypes/convolution.rb +116 -0
  100. data/prototypes/linear_drop.rb +51 -0
  101. data/prototypes/recurrent_layers.rb +79 -0
  102. data/www/images/screamer.png +0 -0
  103. data/www/images/screamer.xcf +0 -0
  104. data/www/index.html +82 -0
  105. metadata +373 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 98b06c828793e0ecc63255ac42b21b1860bb392d
4
+ data.tar.gz: d280b835315f6de205602b40150c6d68e638a42d
5
+ SHA512:
6
+ metadata.gz: 265aef5a22270f7476fae2327e9f0f0d03817a74026c85701ecfb444e468a6ff475b0f58f9ecdaeb03f802d041d29d63716d7e4df9e6695c044099c01d966683
7
+ data.tar.gz: 595de49ff692e78f5b49ddcf01ae510fde8f0227253d00f3b67f9fe6b1a5b2ba533589f14035d006b363bc8b50f39c2e58e6ff6c6c0c1b38345bf8104a9ed693
@@ -0,0 +1,16 @@
1
+ *~
2
+ \#*#
3
+ *.bak
4
+ *.neural[-_]model
5
+ *.coocoo[-_]model
6
+ *.o
7
+ *.so
8
+ *.exe
9
+ *.dll
10
+ doc/spec
11
+ doc/spec.html
12
+ doc/coverage
13
+ ext/buffer/*.cu
14
+ !ext/buffer/*.pre.cu
15
+ ext/buffer/*ffi.rb
16
+ ext/buffer/*.h
@@ -0,0 +1,47 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'coo-coo/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "CooCoo"
8
+ s.version = CooCoo::VERSION
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ["Nolan Eakins"]
11
+ s.email = ["sneakin+coocoo@semanticgap.com"]
12
+ s.homepage = "https://CooCoo.network/"
13
+ s.summary = "Neural networks in Ruby and CUDA."
14
+ s.license = "GPL"
15
+
16
+ if s.respond_to?(:metadata)
17
+ s.metadata['yard.run'] = 'yri'
18
+ else
19
+ raise "RubyGems 2.0 or newer is required to protect against " \
20
+ "public gem pushes."
21
+ end
22
+
23
+ s.files = `git ls-files -z`.split("\x0").reject do |f|
24
+ f.match(%r{^(test|spec|features)/})
25
+ end
26
+ s.bindir = "bin"
27
+ s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ s.require_paths = ["lib"]
29
+
30
+ s.add_development_dependency "bundler", "~> 1.14"
31
+ s.add_development_dependency "rake", "~> 10.0"
32
+ s.add_development_dependency "rspec", "~> 3.0"
33
+ s.add_development_dependency "yard"
34
+ s.add_development_dependency "yard-rspec"
35
+ s.add_development_dependency "pry", "~> 0.11.3"
36
+ s.add_development_dependency 'simplecov'
37
+ s.add_development_dependency 'coderay'
38
+
39
+ s.add_dependency 'nmatrix'
40
+ s.add_dependency 'parallel'
41
+ s.add_dependency 'nokogiri'
42
+ s.add_dependency 'ruby-progressbar'
43
+ s.add_dependency 'chunky_png'
44
+ s.add_dependency 'cairo'
45
+ s.add_dependency 'colorize'
46
+ s.add_dependency 'ffi'
47
+ end
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+ gemspec
3
+
4
+ #gem 'simplecov', :require => false, :group => :test
@@ -0,0 +1,88 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ CooCoo (0.1.0)
5
+ cairo
6
+ chunky_png
7
+ colorize
8
+ ffi
9
+ nmatrix
10
+ nokogiri
11
+ parallel
12
+ ruby-progressbar
13
+
14
+ GEM
15
+ remote: http://rubygems.org/
16
+ specs:
17
+ backports (3.10.3)
18
+ cairo (1.15.11)
19
+ native-package-installer (>= 1.0.3)
20
+ pkg-config (>= 1.2.2)
21
+ cairo (1.15.11-x64-mingw32)
22
+ native-package-installer (>= 1.0.3)
23
+ pkg-config (>= 1.2.2)
24
+ chunky_png (1.3.8)
25
+ coderay (1.1.2)
26
+ colorize (0.8.1)
27
+ diff-lcs (1.3)
28
+ docile (1.1.5)
29
+ ffi (1.9.18)
30
+ ffi (1.9.18-x64-mingw32)
31
+ json (2.1.0)
32
+ method_source (0.9.0)
33
+ mini_portile2 (2.3.0)
34
+ native-package-installer (1.0.6)
35
+ nmatrix (0.2.4)
36
+ packable (~> 1.3, >= 1.3.5)
37
+ nokogiri (1.8.1)
38
+ mini_portile2 (~> 2.3.0)
39
+ nokogiri (1.8.1-x64-mingw32)
40
+ mini_portile2 (~> 2.3.0)
41
+ packable (1.3.9)
42
+ backports
43
+ parallel (1.12.1)
44
+ pkg-config (1.2.8)
45
+ pry (0.11.3)
46
+ coderay (~> 1.1.0)
47
+ method_source (~> 0.9.0)
48
+ rake (10.5.0)
49
+ rspec (3.7.0)
50
+ rspec-core (~> 3.7.0)
51
+ rspec-expectations (~> 3.7.0)
52
+ rspec-mocks (~> 3.7.0)
53
+ rspec-core (3.7.0)
54
+ rspec-support (~> 3.7.0)
55
+ rspec-expectations (3.7.0)
56
+ diff-lcs (>= 1.2.0, < 2.0)
57
+ rspec-support (~> 3.7.0)
58
+ rspec-mocks (3.7.0)
59
+ diff-lcs (>= 1.2.0, < 2.0)
60
+ rspec-support (~> 3.7.0)
61
+ rspec-support (3.7.0)
62
+ ruby-progressbar (1.9.0)
63
+ simplecov (0.15.1)
64
+ docile (~> 1.1.0)
65
+ json (>= 1.8, < 3)
66
+ simplecov-html (~> 0.10.0)
67
+ simplecov-html (0.10.2)
68
+ yard (0.9.12)
69
+ yard-rspec (0.1)
70
+ yard
71
+
72
+ PLATFORMS
73
+ ruby
74
+ x64-mingw32
75
+
76
+ DEPENDENCIES
77
+ CooCoo!
78
+ bundler (~> 1.14)
79
+ coderay
80
+ pry (~> 0.11.3)
81
+ rake (~> 10.0)
82
+ rspec (~> 3.0)
83
+ simplecov
84
+ yard
85
+ yard-rspec
86
+
87
+ BUNDLED WITH
88
+ 1.15.4
@@ -0,0 +1,123 @@
1
+ CooCoo
2
+ ==========
3
+
4
+ Copyright &copy; 2017-2018 [Nolan Eakins](mailto:sneakin+at+semanticgap.com)
5
+
6
+ THIS IS NOT PRODUCTION QUALITY. USE AT YOUR OWN RISK.
7
+
8
+ [Home page](https://coocoo.network/)
9
+ [GitHub](https://github.com/sneakin/CooCoo/)
10
+
11
+
12
+ Description
13
+ ----------
14
+
15
+ A neural network library implemented in Ruby with a CUDA backend.
16
+
17
+
18
+ Dependencies
19
+ -------------
20
+
21
+ * [NVIDIA's CUDA Toolkit](https://developer.nvidia.com/cuda-downloads)
22
+ * Any one of the following C/C++ environments:
23
+ * [GCC](https://gcc.gnu.org/)
24
+ * [MSYS2](http://www.msys2.org/)
25
+ * [Ruby](https://www.ruby-lang.org/): install via your distribution package manager or MSYS2's `pacman -S ruby`
26
+ * [Bundler](http://bundler.io/)
27
+ * [Cairo](https://www.cairographics.org/rcairo/)
28
+
29
+
30
+ Usage
31
+ ----------
32
+
33
+ ### Coming soon
34
+
35
+ $ gem install CooCoo
36
+
37
+
38
+ ### Install
39
+
40
+ First the required dependencies need to be installed. The dependencies include the CUDA compiler and RubyGems.
41
+
42
+ Once the CUDA toolkit is installed, make sure it and Visual Studio's C++ compiler are in your path. Under MSYS2, use something like:
43
+
44
+ $ export PATH=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/2017/Community/VC/Tools/MSVC/14.10.25017/bin/HostX64/x64:$PATH
45
+ $ export PATH=/c/Program\ Files/NVIDIA\ GPU\ Computing\ Toolkit/CUDA/v9.0/bin:$PATH
46
+
47
+ RubyGems are installed with Bundler: `bundle install`
48
+
49
+ And the extension is built with: `rake compile`
50
+
51
+ Then to run an example: `bundle exec ruby -Ilib -Iexamples examples/seeds.rb`
52
+
53
+ Or IRB: `bundle exec irb -Ilib`
54
+
55
+ Or IRB: `bundle exec ruby -Ilib`
56
+
57
+
58
+ ### Code
59
+
60
+ ```ruby
61
+ require 'coo-coo'
62
+
63
+ network = CooCoo::Network.new()
64
+ # create the layers
65
+ network.layer(CooCoo::Layer.new(28 * 28, 100))
66
+ network.layer(CooCoo::Layer.new(100, 10))
67
+
68
+ # learn
69
+ training_data = [ [expected_output, input_data_array ], ...]
70
+
71
+ trainer = CooCoo::Trainer::Stochastic.new
72
+ trainer.train(network: network,
73
+ data: training_data,
74
+ learning_rate: learning_rate,
75
+ batch_size: batch_size) do |stats|
76
+ # called every batch_size
77
+ puts("Batch #{batch} took #{stats.total_time} seconds with an average loss of #{stats.average_loss}.")
78
+ end
79
+
80
+ # store to disk
81
+ network.save("my_first_network.coo-coo_model")
82
+
83
+ # load from disk
84
+ loaded_net = CooCoo::Network.load!("my_first_network.coo-coo_model")
85
+
86
+ # predict
87
+ output = loaded_network.predict([ 0, 0, 0, ... ])
88
+ # => [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
89
+
90
+ ```
91
+
92
+ Examples
93
+ ----------
94
+
95
+ All examples use `OptParse`, so refer to the output of running the example with `--help`. With no arguments, typically a network will be generated, trained, and tested without saving.
96
+
97
+ To run an example: `bundle exec ruby -Ilib -Iexamples examples/EXAMPLE.rb --help`
98
+
99
+ ### [char-rnn](examples/char-rnn.rb)
100
+
101
+ A recursive network that learns byte sequences.
102
+
103
+ ### [UCI Wheat Seed Classifier](examples/seeds.rb)
104
+
105
+ Inspired by the IBAFSIP, this uses the UCI Machine Learning Repository's [wheat seed dataset](http://archive.ics.uci.edu/ml/datasets/seeds) to predict the type of seed given seven parameters.
106
+
107
+ ### [MNIST Classifier](examples/mnist_classifier.rb)
108
+
109
+ [The MNIST Database](http://yann.lecun.com/exdb/mnist/) is used to train a 10 digit classifier.
110
+
111
+ ### [MNIST Dream](examples/mnist_dream.rb)
112
+
113
+ Uses a network trained with the MNIST Classifier to generate valid inputs via backpropagation.
114
+
115
+
116
+ Credits
117
+ ----------
118
+
119
+ Loosely based on [Implement Backpropagation Algorithm From Scratch in Python](http://machinelearningmastery.com/implement-backpropagation-algorithm-scratch-python/)
120
+
121
+ And more than a few of [Siraj Raval's videos](https://www.youtube.com/channel/UCWN3xxRkmTPmbKwht9FuE5A)
122
+
123
+ Cross entropy & Softmax sorted out with [DeepNotes.io](https://deepnotes.io/softmax-crossentropy)
@@ -0,0 +1,81 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task :default => [ 'spec:coverage', 'doc' ]
4
+
5
+ desc "Clean any output files."
6
+ task :clean => :clean_ext do
7
+ sh("rm -rf doc/coverage doc/rdoc doc/spec.html doc/api")
8
+ end
9
+
10
+ task :clean_ext do
11
+ Dir.glob("ext/*").each do |entry|
12
+ sh("cd #{entry}; rake clean") if File.exists?(File.join(entry, "Rakefile"))
13
+ end
14
+ end
15
+
16
+ desc "Compile the extensions."
17
+ if ENV["USE_CUDA"] != "0"
18
+ task :compile => "ext/buffer/Rakefile" do |t|
19
+ pwd = Dir.pwd
20
+ Dir.chdir(File.dirname(t.source))
21
+ sh("rake")
22
+ Dir.chdir(pwd)
23
+ end
24
+ else
25
+ task :compile do
26
+ end
27
+ end
28
+
29
+ desc 'Create the YARDocs'
30
+ require 'yard'
31
+ YARD::Rake::YardocTask.new(:doc) do |t|
32
+ t.files = [ 'lib/**/*.rb', 'examples/**/*.rb', 'spec/**/*.spec', '-', 'README.md' ]
33
+ t.options = [ '--plugin', 'rspec', '-o', 'doc/api' ]
34
+ end
35
+
36
+ desc "Run the rspecs."
37
+ require 'rspec/core/rake_task'
38
+ RSpec::Core::RakeTask.new(:spec => :compile) do |t, args|
39
+ t.pattern = "spec/**/*.spec"
40
+ end
41
+
42
+ namespace :spec do
43
+ desc "Run the specs with HTML output."
44
+ RSpec::Core::RakeTask.new(:html => :compile) do |t, args|
45
+ t.pattern = "spec/**/*.spec"
46
+ t.rspec_opts = '-fhtml -o doc/spec.html'
47
+ end
48
+
49
+ desc "Run the specs with code coverage."
50
+ task :coverage => :compile do
51
+ ENV['COVERAGE'] = 'true'
52
+ Rake::Task['spec:html'].execute
53
+ end
54
+ end
55
+
56
+ desc "Run Ruby with everything in the search paths."
57
+ task :run => :compile do
58
+ args = $*[1, $*.size - 1]
59
+ exec("bundle exec ruby -Ilib -Iexamples #{args.join(' ')}")
60
+ end
61
+
62
+ desc "Start an IRB session with everything loaded."
63
+ task :shell => :compile do
64
+ exec("bundle exec irb -Ilib -Iexamples -rcoo-coo/shell")
65
+ end
66
+
67
+ desc "Start a Pry session with everything loaded."
68
+ task :pry => :compile do
69
+ exec("bundle exec pry -Ilib -Iexamples -rcoo-coo/shell")
70
+ end
71
+
72
+ namespace :www do
73
+ desc "Upload the website"
74
+ task :upload do
75
+ user = ENV.fetch("COOCOO_USER")
76
+ sh("ssh #{user}@coocoo.network mkdir -p \\~/www/coocoo.network/public/images")
77
+ sh("scp www/index.html #{user}@coocoo.network:~/www/coocoo.network/public/index.html")
78
+ sh("scp www/images/screamer.png #{user}@coocoo.network:~/www/coocoo.network/public/images/screamer.png")
79
+ end
80
+ end
81
+
@@ -0,0 +1,25 @@
1
+ #!/bin/env ruby
2
+
3
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'coo-coo/cuda'
5
+
6
+ unless CooCoo::CUDA.available?
7
+ puts("Cuda not available")
8
+ exit(-1)
9
+ end
10
+
11
+ puts("Device\t#{CooCoo::CUDA::Runtime.get_device} / #{CooCoo::CUDA::Runtime.device_count}")
12
+ props = CooCoo::CUDA::Runtime::DeviceProperties.new
13
+ err = CooCoo::CUDA::Runtime.cudaGetDeviceProperties(props, 0)
14
+ raise CUDAError(err) if err != 0
15
+
16
+ props.members.each do |m|
17
+ value = props[m]
18
+ if m != :name && value.kind_of?(FFI::Struct::InlineArray)
19
+ value.each_with_index do |v, i|
20
+ puts("#{m}[#{i}]\t#{v}")
21
+ end
22
+ else
23
+ puts("#{m}\t#{value}")
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ #!/bin/env ruby
2
+
3
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'coo-coo/cuda'
5
+
6
+ require 'ostruct'
7
+ options = OpenStruct.new
8
+ options.follow = ARGV[0] == '-f'
9
+ options.timeout = (ARGV[1] || 1).to_f
10
+
11
+ unless options.follow
12
+ puts("Device\tBytes Free\tBytes Total\tPercent")
13
+ end
14
+
15
+ follow = true
16
+ $stdout.sync = true
17
+
18
+ while follow
19
+ follow = options.follow
20
+ CooCoo::CUDA::Runtime.device_count.times do |device|
21
+ CooCoo::CUDA::Runtime.set_device(device)
22
+ free, total = CooCoo::CUDA.memory_info
23
+ $stdout.puts("%i\t%i\t%i\t%.3f" % [ device, free, total, (100.0 * free / total.to_f) ])
24
+ end
25
+
26
+ $stdout.flush
27
+ sleep(options.timeout)
28
+ end
@@ -0,0 +1,7 @@
1
+ #!/bin/zsh
2
+
3
+ ./bin/cuda-free -f 0.25 |
4
+ stdbuf -o0 cut -f 4 |
5
+ trend -t "${TREND_TITLE:-Free CUDA Memory}" -c 1a -geometry ${TREND_GEOMETRY:-640x64-0-24} - ${TREND_SIZE:-$((4*60*4*5))x2} 0 100
6
+
7
+ [[ -z `jobs -p` ]] || kill $(jobs -p)
@@ -0,0 +1,267 @@
1
+ #!/bin/env ruby
2
+
3
+ require 'ffi'
4
+
5
+ class ExportScanner
6
+ NEEDLE = "PUBLIC"
7
+ NVCCFLAGS = []
8
+
9
+ attr_reader :structs
10
+ attr_reader :functions
11
+
12
+ def initialize(typedefs = Hash.new)
13
+ @functions = Array.new
14
+ @structs = Array.new
15
+ @typedefs = typedefs
16
+ end
17
+
18
+ FunctionPrototype = Struct.new(:name, :return_type, :args, :definition)
19
+
20
+ class Argument
21
+ attr_accessor :const
22
+ attr_accessor :type
23
+ attr_accessor :name
24
+ attr_accessor :pointer
25
+
26
+ def initialize(str)
27
+ m = str.match(/\s*(((\w+) )+\s*(([*]+)?(\w+)?)\s*)/m)
28
+ if m
29
+ self.name = m[-1]
30
+ self.const = m[0] =~ /const/
31
+ self.pointer = m[-2]
32
+ self.type = m[3]
33
+ end
34
+ end
35
+ end
36
+
37
+ def parse_args(arg_str)
38
+ args = arg_str.split(',').collect(&:strip)
39
+ arg_types = args.collect do |a|
40
+ Argument.new(a)
41
+ end
42
+ end
43
+
44
+ def scan_function(f)
45
+ m = f.match(/(\w+)\s+(\w+)\s*\((.*)\)/)
46
+ if m
47
+ func = m[2]
48
+ args = parse_args(m[3])
49
+ ret = m[1].strip
50
+ FunctionPrototype.new(func, ret, args, f)
51
+ end
52
+ end
53
+
54
+ def scan_for_functions(path)
55
+ `nvcc -E -DIN_PUBLIC=1 -D#{NEEDLE}=#{NEEDLE} #{NVCCFLAGS.join(' ')} #{path} | grep -e '#{NEEDLE}'`.
56
+ split("\n").
57
+ collect { |f| m = f.match(/(#{NEEDLE}.*[)]([^;]|$))/); m && m[1] }.
58
+ reject(&:nil?).
59
+ collect { |f| scan_function(f[0, f.index(')') + 1]) }
60
+ end
61
+
62
+ StructDef = Struct.new(:name, :definition, :fields)
63
+ StructField = Struct.new(:name, :definition, :type, :pointer)
64
+
65
+ def scan_struct_field(field)
66
+ m = field.match(/\s*(((\w+) )+\s*(([*]+)?(\w+))\s*)/m)
67
+ if m
68
+ StructField.new(m[-1], field, m[2].strip, m[-2])
69
+ end
70
+ end
71
+
72
+ def scan_struct_fields(fields)
73
+ fields.collect { |field| scan_struct_field(field) }.reject(&:nil?)
74
+ end
75
+
76
+ def scan_for_structs_inner(data, acc = [])
77
+ m = data.match(/(typedef\s+struct\s+(\w+)\s+{(.*)}\s+[*](\w+);)(.*)/m)
78
+ if m
79
+ acc << StructDef.new(m[4], m[1], scan_struct_fields(m[3].split(';').collect(&:strip)))
80
+ scan_for_structs_inner(m[5], acc)
81
+ else
82
+ acc
83
+ end
84
+ end
85
+
86
+ def scan_for_structs(path)
87
+ scan_for_structs_inner(File.read(path))
88
+ end
89
+
90
+ def scan(path)
91
+ @functions += scan_for_functions(path)
92
+ @structs += scan_for_structs(path)
93
+
94
+ self
95
+ end
96
+
97
+ def header(name)
98
+ head = File.basename(name).upcase
99
+ <<-EOT
100
+ \#ifndef #{head}
101
+ \#define #{head}
102
+
103
+ \#include "public.h"
104
+
105
+ extern "C" {
106
+ #{structs.collect(&:definition).join('\n')}
107
+
108
+ #{functions.collect(&:definition).join(";\n ")};
109
+ }
110
+
111
+ \#endif /* #{head} */
112
+ EOT
113
+ end
114
+
115
+ def ffi_type(a, pointer = nil)
116
+ a = @typedefs[a] if @typedefs.has_key?(a)
117
+ if FFI::TypeDefs[a.to_sym]
118
+ ":" + FFI::TypeDefs.key(FFI::TypeDefs[a.to_sym]).to_s
119
+ elsif pointer
120
+ "#{a}.auto_ptr"
121
+ else
122
+ a
123
+ end
124
+ end
125
+
126
+ def ffi_def(f)
127
+ func = f.name
128
+ args = f.args.collect do |a|
129
+ if a.pointer
130
+ ":pointer"
131
+ else
132
+ ffi_type(a.type)
133
+ end
134
+ end
135
+
136
+ ret = f.return_type
137
+ ret = ffi_type(ret, true)
138
+
139
+ " attach_function :#{func}, [ #{args.join(', ')} ], #{ret}"
140
+ end
141
+
142
+ def ffi_struct(s)
143
+ layout = s.fields.collect { |f| [ ":#{f.name}", ffi_type(f.type, f.pointer) ] }
144
+ <<-EOT
145
+ class #{s.name} < ::FFI::Struct
146
+ layout(#{layout.join(', ')})
147
+
148
+ def self.release(ptr)
149
+ #{s.name.downcase}_free(ptr)
150
+ end
151
+ end
152
+ EOT
153
+ end
154
+
155
+ def ffi(name, library)
156
+ <<-EOT
157
+ module #{name}
158
+ module FFI
159
+ extend ::FFI::Library
160
+ ffi_lib Pathname.new(__FILE__).join('..', "#{library}.#{RbConfig::CONFIG['DLEXT']}").to_s
161
+
162
+ #{structs.collect { |s| ffi_struct(s) }.compact.join("\n")}
163
+ #{functions.collect { |f| ffi_def(f) }.compact.join("\n")}
164
+ end
165
+ end
166
+ EOT
167
+ end
168
+ end
169
+
170
+ def write_header(header, sources)
171
+ # empty the file for preprocessing
172
+ if header != nil && header != '-'
173
+ File.open(header, 'w') do |f|
174
+ f.puts
175
+ end
176
+ end
177
+
178
+ scanner = ExportScanner.new
179
+ sources.each(&-> (s) { scanner.scan(s) })
180
+
181
+ if header == nil || header == '-'
182
+ $stdout.puts(scanner.header("STDOUT"))
183
+ else
184
+ File.open(header, 'w') do |f|
185
+ f.puts(scanner.header(header.gsub('.', '_')))
186
+ end
187
+ end
188
+ end
189
+
190
+ def write_ffi(mod, library, output, sources, typedefs)
191
+ raise ArgumentError.new('No --module name given.') if mod == nil
192
+ raise ArgumentError.new('No --library given.') if library == nil
193
+
194
+ scanner = ExportScanner.new(typedefs)
195
+ sources.each(&->(s) { scanner.scan(s) })
196
+
197
+ if output == nil || output == '-'
198
+ $stdout.puts(scanner.ffi(mod, library))
199
+ else
200
+ File.open(output, 'w') do |f|
201
+ f.puts(scanner.ffi(mod, library))
202
+ end
203
+ end
204
+ end
205
+
206
+ require 'fileutils'
207
+ def write_source(output, source)
208
+ FileUtils.copy(source, output)
209
+ end
210
+
211
+
212
+ if __FILE__ == $0
213
+ require 'ostruct'
214
+ options = OpenStruct.new
215
+ options.mode = :help
216
+ options.sources = Array.new
217
+ options.typedefs = Hash.new
218
+
219
+ require 'optparse'
220
+ opts = OptionParser.new() do |o|
221
+ o.on('-h', 'Help') do
222
+ options.mode = :help
223
+ end
224
+
225
+ o.on('--header', 'Generate a C/C++ header.') do
226
+ options.mode = :header
227
+ end
228
+
229
+ o.on('--ffi', 'Generate a Ruby FFI.') do
230
+ options.mode = :ffi
231
+ end
232
+
233
+ o.on('--source', 'Generate C/C++ source code.') do
234
+ options.mode = :source
235
+ end
236
+
237
+ o.on('-o', '--output PATH', 'Write the output to PATH.') do |path|
238
+ options.output = path
239
+ end
240
+
241
+ o.on('--module NAME', 'The name of the generated FFI module.') do |m|
242
+ options.module = m
243
+ end
244
+
245
+ o.on('-l', '--library NAME', 'The shared library name to load.') do |l|
246
+ options.library = l
247
+ end
248
+
249
+ o.on('-t', '--typedef TYPE=NAME') do |v|
250
+ type, name = v.split('=')
251
+ options.typedefs[name] = type if type && name
252
+ end
253
+ end
254
+
255
+ options.sources = opts.parse!(ARGV)
256
+
257
+ unless options.mode == :help
258
+ raise ArgumentError.new("No sources found.") unless options.sources.size > 0
259
+ end
260
+
261
+ case options.mode
262
+ when :ffi then write_ffi(options.module, options.library, options.output, options.sources, options.typedefs)
263
+ when :header then write_header(options.output, options.sources)
264
+ when :source then write_source(options.output, options.sources[0])
265
+ else puts(opts.help)
266
+ end
267
+ end