CooCoo 0.1.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 (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