matrix_boost 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +54 -0
- data/RELEASE.md +12 -0
- data/Rakefile +26 -0
- data/bin/bundle +105 -0
- data/bin/console +14 -0
- data/bin/rake +29 -0
- data/bin/setup +8 -0
- data/ext/extconf.rb +3 -0
- data/ext/extension.c +72 -0
- data/ext/matrices.c +111 -0
- data/ext/matrices.h +24 -0
- data/lib/matrix_boost.rb +36 -0
- data/lib/matrix_boost/core_extensions.rb +19 -0
- data/lib/matrix_boost/version.rb +3 -0
- data/matrix_boost.gemspec +37 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b309404421f2759c33ecc81ed7fcd0a97d4523189dbd0cddae4605708b1098cb
|
4
|
+
data.tar.gz: fa48071d459c9a49faf9f3485e8501878e3bf0b75ad9e64539e6291265986acc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d07a2ab0fe8daf2da74e8ca9420c29166d8df42afdce783b601d3235d362eb55e7cbd587b3b904c26f9c1dcbfc378c386bd4857bf93ec2ebbedb551ad93921fd
|
7
|
+
data.tar.gz: 8245b8b399a23e7b0698df9485e63e2ae3e3ac12209732e615cfe74c39145d0d972a8c935fd294004684d106d93418d85978bd8aec8dc2398b83d4dd41292238
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [v0.1.0](https://github.com/Bajena/matrix_boost/tree/v0.1.0) (2020-04-12)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/Bajena/matrix_boost/compare/8f26eddc7e648c0c68375b52b736535126bfd47f...v0.1.0)
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Jan Bajena
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# MatrixBoost
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.com/Bajena/matrix_boost.svg?branch=master)](https://travis-ci.com/Bajena/matrix_boost)
|
4
|
+
|
5
|
+
This gem showcases usage of Ruby C extensions by reimplementing operations
|
6
|
+
from Ruby's [https://ruby-doc.org/stdlib-2.5.1/libdoc/matrix/rdoc/Matrix.html](Matrix) library.
|
7
|
+
|
8
|
+
### Performance comparison
|
9
|
+
|
10
|
+
```bash
|
11
|
+
irb(main):007:0> MatrixBoost.benchmark(dim: 3, n: 10000000)
|
12
|
+
user system total real
|
13
|
+
Ruby matrix multiply: 81.385777 0.190162 81.575939 ( 81.751942)
|
14
|
+
C matrix multiply: 47.041961 0.081723 47.123684 ( 47.206611)
|
15
|
+
Ruby matrix multiply after monkey patch: 50.903808 0.222949 51.126757 ( 51.351058
|
16
|
+
```
|
17
|
+
|
18
|
+
as you can see Ruby's Matrix implementation is ~67% slower than the same operation
|
19
|
+
implemented in a C extension 🎉.
|
20
|
+
|
21
|
+
### Matrix class core extension
|
22
|
+
Even though this gem was created mainly for learning how to use C extensions in Ruby
|
23
|
+
you should still be able to use it in your production code.
|
24
|
+
|
25
|
+
You can either use `MatrixBoost.multiply(m1, m2)` or replace the original methods
|
26
|
+
from `Matrix` by calling `MatrixBoost.apply_core_extensions`.
|
27
|
+
|
28
|
+
### How do I play around?
|
29
|
+
|
30
|
+
```bash
|
31
|
+
$ bin/setup
|
32
|
+
$ bin/rake compile
|
33
|
+
```
|
34
|
+
|
35
|
+
And then run this play around:
|
36
|
+
|
37
|
+
```bash
|
38
|
+
$ bin/console
|
39
|
+
```
|
40
|
+
|
41
|
+
```bash
|
42
|
+
irb(main):001:0> m1 = Matrix[[1, 2, 3], [4, 5, 6]]
|
43
|
+
=> Matrix[[1, 2, 3], [4, 5, 6]]
|
44
|
+
irb(main):002:0> m2 = Matrix[[9, 8],[7, 6],[5, 4]]
|
45
|
+
=> Matrix[[9, 8], [7, 6], [5, 4]]
|
46
|
+
irb(main):003:0> MatrixBoost.multiply(m1, m2)
|
47
|
+
=> Matrix[[38.0, 32.0], [101.0, 86.0]]
|
48
|
+
```
|
49
|
+
|
50
|
+
Or run the tests:
|
51
|
+
|
52
|
+
```bash
|
53
|
+
bin/rake test
|
54
|
+
```
|
data/RELEASE.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Release steps
|
2
|
+
1. Bump VERSION constant
|
3
|
+
2. Generate changelog and update
|
4
|
+
```shell
|
5
|
+
CHANGELOG_GITHUB_TOKEN=<token> bundle exec rake changelog
|
6
|
+
```
|
7
|
+
3. Commit & push a new tag
|
8
|
+
4. Build and push to rubygems
|
9
|
+
```shell
|
10
|
+
gem build matrix_boost
|
11
|
+
gem push matrix_boost-x.y.z.gem
|
12
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
require "matrix_boost/version"
|
4
|
+
require "github_changelog_generator/task"
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
10
|
+
end
|
11
|
+
|
12
|
+
task :compile do
|
13
|
+
puts "Compiling extension"
|
14
|
+
`cd ext && make clean`
|
15
|
+
`cd ext && ruby extconf.rb`
|
16
|
+
`cd ext && make`
|
17
|
+
puts "Done"
|
18
|
+
end
|
19
|
+
|
20
|
+
GitHubChangelogGenerator::RakeTask.new :changelog do |config|
|
21
|
+
config.user = "Bajena"
|
22
|
+
config.project = "matrix_boost"
|
23
|
+
config.future_release = "v#{MatrixBoost::VERSION}"
|
24
|
+
end
|
25
|
+
|
26
|
+
task :default => :test
|
data/bin/bundle
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'bundle' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "rubygems"
|
12
|
+
|
13
|
+
m = Module.new do
|
14
|
+
module_function
|
15
|
+
|
16
|
+
def invoked_as_script?
|
17
|
+
File.expand_path($0) == File.expand_path(__FILE__)
|
18
|
+
end
|
19
|
+
|
20
|
+
def env_var_version
|
21
|
+
ENV["BUNDLER_VERSION"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def cli_arg_version
|
25
|
+
return unless invoked_as_script? # don't want to hijack other binstubs
|
26
|
+
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
27
|
+
bundler_version = nil
|
28
|
+
update_index = nil
|
29
|
+
ARGV.each_with_index do |a, i|
|
30
|
+
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
|
31
|
+
bundler_version = a
|
32
|
+
end
|
33
|
+
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
34
|
+
bundler_version = $1 || ">= 0.a"
|
35
|
+
update_index = i
|
36
|
+
end
|
37
|
+
bundler_version
|
38
|
+
end
|
39
|
+
|
40
|
+
def gemfile
|
41
|
+
gemfile = ENV["BUNDLE_GEMFILE"]
|
42
|
+
return gemfile if gemfile && !gemfile.empty?
|
43
|
+
|
44
|
+
File.expand_path("../../Gemfile", __FILE__)
|
45
|
+
end
|
46
|
+
|
47
|
+
def lockfile
|
48
|
+
lockfile =
|
49
|
+
case File.basename(gemfile)
|
50
|
+
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
|
51
|
+
else "#{gemfile}.lock"
|
52
|
+
end
|
53
|
+
File.expand_path(lockfile)
|
54
|
+
end
|
55
|
+
|
56
|
+
def lockfile_version
|
57
|
+
return unless File.file?(lockfile)
|
58
|
+
lockfile_contents = File.read(lockfile)
|
59
|
+
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
60
|
+
Regexp.last_match(1)
|
61
|
+
end
|
62
|
+
|
63
|
+
def bundler_version
|
64
|
+
@bundler_version ||= begin
|
65
|
+
env_var_version || cli_arg_version ||
|
66
|
+
lockfile_version || "#{Gem::Requirement.default}.a"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_bundler!
|
71
|
+
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
72
|
+
|
73
|
+
# must dup string for RG < 1.8 compatibility
|
74
|
+
activate_bundler(bundler_version.dup)
|
75
|
+
end
|
76
|
+
|
77
|
+
def activate_bundler(bundler_version)
|
78
|
+
if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0")
|
79
|
+
bundler_version = "< 2"
|
80
|
+
end
|
81
|
+
gem_error = activation_error_handling do
|
82
|
+
gem "bundler", bundler_version
|
83
|
+
end
|
84
|
+
return if gem_error.nil?
|
85
|
+
require_error = activation_error_handling do
|
86
|
+
require "bundler/version"
|
87
|
+
end
|
88
|
+
return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
89
|
+
warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`"
|
90
|
+
exit 42
|
91
|
+
end
|
92
|
+
|
93
|
+
def activation_error_handling
|
94
|
+
yield
|
95
|
+
nil
|
96
|
+
rescue StandardError, LoadError => e
|
97
|
+
e
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
m.load_bundler!
|
102
|
+
|
103
|
+
if m.invoked_as_script?
|
104
|
+
load Gem.bin_path("bundler", "bundle")
|
105
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "matrix_boost"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/setup
ADDED
data/ext/extconf.rb
ADDED
data/ext/extension.c
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
#include "ruby/ruby.h"
|
2
|
+
#include "ruby/encoding.h"
|
3
|
+
#include "matrices.h"
|
4
|
+
|
5
|
+
void print_obj(VALUE o) {
|
6
|
+
VALUE os = rb_funcall(o, rb_intern("to_s"), 0);
|
7
|
+
puts(StringValuePtr(os));
|
8
|
+
}
|
9
|
+
|
10
|
+
VALUE matrix_to_rb_array(Matrix *matrix) {
|
11
|
+
VALUE result = rb_ary_new();
|
12
|
+
int row;
|
13
|
+
int column;
|
14
|
+
|
15
|
+
for (row = 0; row < matrix->rows; row++) {
|
16
|
+
VALUE rb_row = rb_ary_new();
|
17
|
+
rb_ary_push(result, rb_row);
|
18
|
+
for (column = 0; column < matrix->columns; column++) {
|
19
|
+
rb_ary_push(rb_row, DBL2NUM((matrix->numbers)[column][row]));
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
return result;
|
24
|
+
}
|
25
|
+
|
26
|
+
Matrix* rb_array_to_matrix(VALUE a) {
|
27
|
+
long rows = RARRAY_LEN(a);
|
28
|
+
VALUE first_row = rb_ary_entry(a, 0);
|
29
|
+
Check_Type(first_row, T_ARRAY);
|
30
|
+
long columns = RARRAY_LEN(first_row);
|
31
|
+
|
32
|
+
Matrix *matrix = constructor(rows, columns);
|
33
|
+
int row;
|
34
|
+
int column;
|
35
|
+
|
36
|
+
for (row = 0; row < rows; row++) {
|
37
|
+
VALUE rb_row = rb_ary_entry(a, row);
|
38
|
+
for (column = 0; column < columns; column++) {
|
39
|
+
VALUE rb_row = rb_ary_entry(a, row);
|
40
|
+
(matrix->numbers)[column][row] = NUM2DBL(rb_ary_entry(rb_row, column));
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
return matrix;
|
45
|
+
}
|
46
|
+
|
47
|
+
static VALUE mul_matrix(VALUE self, VALUE m1, VALUE m2) {
|
48
|
+
Check_Type(m1, T_ARRAY);
|
49
|
+
Check_Type(m2, T_ARRAY);
|
50
|
+
|
51
|
+
Matrix *m1c = rb_array_to_matrix(m1);
|
52
|
+
Matrix *m2c = rb_array_to_matrix(m2);
|
53
|
+
|
54
|
+
Matrix *multiplied = multiply(m1c, m2c);
|
55
|
+
|
56
|
+
destroy_matrix(m1c);
|
57
|
+
destroy_matrix(m2c);
|
58
|
+
|
59
|
+
if (multiplied) {
|
60
|
+
VALUE result = matrix_to_rb_array(multiplied);
|
61
|
+
destroy_matrix(multiplied);
|
62
|
+
return result;
|
63
|
+
} else {
|
64
|
+
return Qnil;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
void Init_extension(void) {
|
69
|
+
VALUE MatrixBoost = rb_define_module("MatrixBoost");
|
70
|
+
VALUE NativeHelpers = rb_define_class_under(MatrixBoost, "NativeHelpers", rb_cObject);
|
71
|
+
rb_define_singleton_method(NativeHelpers, "mul_matrix", mul_matrix, 2);
|
72
|
+
}
|
data/ext/matrices.c
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
#include<math.h>
|
2
|
+
#include<stdlib.h>
|
3
|
+
#include"matrices.h"
|
4
|
+
#include<stdio.h>
|
5
|
+
#include<time.h>
|
6
|
+
|
7
|
+
#define SUCC 1
|
8
|
+
#define FAIL -1
|
9
|
+
/*
|
10
|
+
* This library was originaly posted at https://github.com/nhomble/yasML
|
11
|
+
*
|
12
|
+
* A matrix is
|
13
|
+
columns
|
14
|
+
pointer . . . .
|
15
|
+
rows |
|
16
|
+
|
|
17
|
+
V
|
18
|
+
.
|
19
|
+
.
|
20
|
+
.
|
21
|
+
the matrix is an array of array pointers where each array pointer corresponds to a vector
|
22
|
+
*/
|
23
|
+
|
24
|
+
static double vector_multiply(double *col, double *row, int length);
|
25
|
+
|
26
|
+
/* make a zero matrix of given dimensions */
|
27
|
+
Matrix *constructor(int r, int c){
|
28
|
+
unsigned int i;
|
29
|
+
Matrix *m;
|
30
|
+
if(r <= 0 || c <= 0){
|
31
|
+
perror("Give me positive values for dimensions genius");
|
32
|
+
return NULL;
|
33
|
+
}
|
34
|
+
m = malloc(sizeof(Matrix));
|
35
|
+
m->rows = r;
|
36
|
+
m->columns = c;
|
37
|
+
m->numbers = malloc(sizeof(double *)*c);
|
38
|
+
for(i = 0; i < c; i++)
|
39
|
+
m->numbers[i] = calloc(sizeof(double), r);
|
40
|
+
return m;
|
41
|
+
}
|
42
|
+
|
43
|
+
/* free memory associated with the matrix */
|
44
|
+
int destroy_matrix(Matrix *m){
|
45
|
+
unsigned int i;
|
46
|
+
if(m == NULL)
|
47
|
+
return FAIL;
|
48
|
+
for(i = 0; i < m->columns; i++)
|
49
|
+
free(m->numbers[i]);
|
50
|
+
free(m->numbers);
|
51
|
+
free(m);
|
52
|
+
return SUCC;
|
53
|
+
}
|
54
|
+
|
55
|
+
/* print the matrix */
|
56
|
+
int print(Matrix *m){
|
57
|
+
unsigned int i, j;
|
58
|
+
if(m == NULL)
|
59
|
+
return FAIL;
|
60
|
+
for(i = 0; i < m->rows; i++){
|
61
|
+
for(j = 0; j < m->columns; j++){
|
62
|
+
printf("%f ", m->numbers[j][i]);
|
63
|
+
}
|
64
|
+
printf("\n");
|
65
|
+
}
|
66
|
+
return SUCC;
|
67
|
+
}
|
68
|
+
|
69
|
+
Matrix *transpose(Matrix *m){
|
70
|
+
Matrix *trans;
|
71
|
+
unsigned int i, j;
|
72
|
+
if(m == NULL)
|
73
|
+
return NULL;
|
74
|
+
trans = constructor(m->columns, m->rows);
|
75
|
+
for(i = 0; i < trans->columns; i++){
|
76
|
+
for(j = 0; j < trans->rows; j++)
|
77
|
+
trans->numbers[i][j] = m->numbers[j][i];
|
78
|
+
}
|
79
|
+
return trans;
|
80
|
+
}
|
81
|
+
|
82
|
+
/* m1 x m2 */
|
83
|
+
Matrix *multiply(Matrix *m1, Matrix *m2){
|
84
|
+
Matrix *product, *trans;
|
85
|
+
unsigned int i, j;
|
86
|
+
if(m1 == NULL || m2 == NULL)
|
87
|
+
return NULL;
|
88
|
+
if(m1->columns != m2->rows)
|
89
|
+
return NULL;
|
90
|
+
trans = transpose(m1);
|
91
|
+
product = constructor(m1->rows, m2->columns);
|
92
|
+
for(i = 0; i < product->columns; i++){
|
93
|
+
for(j = 0; j < product->rows; j++){
|
94
|
+
product->numbers[i][j] = vector_multiply(trans->numbers[j], m2->numbers[i], m2->rows);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
destroy_matrix(trans);
|
98
|
+
return product;
|
99
|
+
}
|
100
|
+
|
101
|
+
/* v1 x v2 -- simply a helper function -- computes dot product between two vectors*/
|
102
|
+
static double vector_multiply(double *col, double *row, int length){
|
103
|
+
double sum;
|
104
|
+
unsigned int i;
|
105
|
+
sum = 0;
|
106
|
+
for(i = 0; i < length; i++){
|
107
|
+
sum += col[i] * row[i];
|
108
|
+
}
|
109
|
+
return sum;
|
110
|
+
}
|
111
|
+
|
data/ext/matrices.h
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#ifndef matrices_h
|
2
|
+
#define matrices_h
|
3
|
+
|
4
|
+
#ifdef __cplusplus
|
5
|
+
extern "C" {
|
6
|
+
#endif
|
7
|
+
/* current representation of a matrix in my mind */
|
8
|
+
typedef struct Matrix{
|
9
|
+
int rows;
|
10
|
+
int columns;
|
11
|
+
double **numbers;
|
12
|
+
} Matrix;
|
13
|
+
|
14
|
+
Matrix *constructor(int r, int c);
|
15
|
+
int destroy_matrix(Matrix *m);
|
16
|
+
int print(Matrix *m);
|
17
|
+
Matrix *transpose(Matrix *m);
|
18
|
+
Matrix *multiply(Matrix *m1, Matrix *m2);
|
19
|
+
|
20
|
+
#ifdef __cplusplus
|
21
|
+
}
|
22
|
+
#endif
|
23
|
+
|
24
|
+
#endif /* matrices */
|
data/lib/matrix_boost.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "extension"
|
2
|
+
require "matrix"
|
3
|
+
require "benchmark"
|
4
|
+
|
5
|
+
module MatrixBoost
|
6
|
+
class << self
|
7
|
+
# @param m1 [Matrix] Stdlib Matrix instance
|
8
|
+
# @param m2 [Matrix] Stdlib Matrix instance
|
9
|
+
# @return [Matrix] m1 x m2 Matrix
|
10
|
+
def multiply(m1, m2)
|
11
|
+
Matrix[*NativeHelpers.mul_matrix(m1.to_a, m2.to_a)]
|
12
|
+
end
|
13
|
+
|
14
|
+
def benchmark(dim: 3, n: 100000)
|
15
|
+
m1 = Matrix[[1, 2, 3], [4, 5, 6]]
|
16
|
+
m2 = Matrix[[9,8],[7,6],[5,4]]
|
17
|
+
|
18
|
+
m1 = Matrix.build(dim) { rand }
|
19
|
+
m2 = Matrix.build(dim) { rand }
|
20
|
+
|
21
|
+
Benchmark.benchmark(Benchmark::CAPTION, 45, Benchmark::FORMAT) do |x|
|
22
|
+
x.report("Ruby matrix multiply:") { n.times { m1 * m2 } }
|
23
|
+
x.report("C matrix multiply:") { n.times { multiply(m1, m2) } }
|
24
|
+
|
25
|
+
apply_core_extensions
|
26
|
+
x.report("Ruby matrix multiply after monkey patch:") { n.times { m1 * m2 } }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def apply_core_extensions
|
31
|
+
require "matrix_boost/core_extensions"
|
32
|
+
|
33
|
+
Matrix.prepend MatrixBoost::CoreExtensions::Multiply
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "matrix"
|
2
|
+
|
3
|
+
module MatrixBoost
|
4
|
+
module CoreExtensions
|
5
|
+
module Multiply
|
6
|
+
# Overrides https://github.com/ruby/matrix/blob/master/lib/matrix.rb#L1057
|
7
|
+
def *(m)
|
8
|
+
case(m)
|
9
|
+
when Matrix
|
10
|
+
raise ErrDimensionMismatch if column_count != m.row_count
|
11
|
+
|
12
|
+
MatrixBoost.multiply(self, m)
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "matrix_boost/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "matrix_boost"
|
7
|
+
spec.version = MatrixBoost::VERSION
|
8
|
+
spec.authors = ["Jan Bajena"]
|
9
|
+
|
10
|
+
spec.summary = "Boosts stdlib Matrix with C extensions"
|
11
|
+
spec.homepage = "https://github.com/Bajena/matrix_boost"
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
15
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
16
|
+
if spec.respond_to?(:metadata)
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/Bajena/matrix_boost"
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/Bajena/matrix_boost/blob/master/CHANGELOG.md"
|
20
|
+
else
|
21
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
22
|
+
"public gem pushes."
|
23
|
+
end
|
24
|
+
|
25
|
+
# Specify which files should be added to the gem when it is released.
|
26
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
27
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
28
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
29
|
+
end
|
30
|
+
spec.bindir = "exe"
|
31
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib", "ext"]
|
33
|
+
|
34
|
+
spec.add_development_dependency "rake", ">= 12.3.3"
|
35
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
36
|
+
spec.add_development_dependency "github_changelog_generator"
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: matrix_boost
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jan Bajena
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-04-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 12.3.3
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 12.3.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: github_changelog_generator
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- ".gitignore"
|
62
|
+
- ".travis.yml"
|
63
|
+
- CHANGELOG.md
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- RELEASE.md
|
68
|
+
- Rakefile
|
69
|
+
- bin/bundle
|
70
|
+
- bin/console
|
71
|
+
- bin/rake
|
72
|
+
- bin/setup
|
73
|
+
- ext/extconf.rb
|
74
|
+
- ext/extension.c
|
75
|
+
- ext/matrices.c
|
76
|
+
- ext/matrices.h
|
77
|
+
- lib/matrix_boost.rb
|
78
|
+
- lib/matrix_boost/core_extensions.rb
|
79
|
+
- lib/matrix_boost/version.rb
|
80
|
+
- matrix_boost.gemspec
|
81
|
+
homepage: https://github.com/Bajena/matrix_boost
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata:
|
85
|
+
homepage_uri: https://github.com/Bajena/matrix_boost
|
86
|
+
source_code_uri: https://github.com/Bajena/matrix_boost
|
87
|
+
changelog_uri: https://github.com/Bajena/matrix_boost/blob/master/CHANGELOG.md
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
- ext
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
requirements: []
|
104
|
+
rubygems_version: 3.0.6
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: Boosts stdlib Matrix with C extensions
|
108
|
+
test_files: []
|