libmf 0.2.2 → 0.2.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e654382e818f1b59bc4437e71bc3e0ae6f4d4d79b9c85aeb53fb3494b2baf888
4
- data.tar.gz: 560fa519794c7cd8b29c27b9ac9f4247e0485e9f13f229c18a4562c8bee62868
3
+ metadata.gz: 33b8ba1c7cf9de8e19fa9775ecdb445bc0f2f4323401cccbf016ff6a81a04a00
4
+ data.tar.gz: 9ff88bdafac8bbfe0c99ccc0c0b7dfb7a9092246de1105c7f99ccade29b9c88b
5
5
  SHA512:
6
- metadata.gz: d16fb17f9b58cea5c53814a68fbf9de91440fd77e1669b52bbe5cb3a3837c94449d8bbc348405c0c117f219d2a513f1fc2e813b330ea127c93ac04a0c4101d07
7
- data.tar.gz: 3d5d3962d3878fe992f76ef9d2fa5e30866d007cd5ae3ab97a666fc53f97f9f3f2487bb425e15b59becde6118c61ecac6c8511692742d64efa2b9e26fe0d2872
6
+ metadata.gz: 021eca76e96ceca2cd484b7f7796a649529f3b20c80d494143cfaf43e617d23ac55ae50bdcd345808e277b31d304ea6e6a53c937b3e9a200f47ae226f27e4f2b
7
+ data.tar.gz: 916b78a5e62d19e4d6a9e1433350bb26982d43393029e4e883ed803f89a03f6db766ab10718120a73db1f0ad7ac80607d475a10a7c203d117ca1706e80618345
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 0.2.6 (2021-12-02)
2
+
3
+ - Improved ARM detection
4
+
5
+ ## 0.2.5 (2021-10-18)
6
+
7
+ - Added named loss functions
8
+ - Added `Matrix` class
9
+ - Improved error checking for `fit`, `cv`, `save_model`, and `load_model`
10
+
11
+ ## 0.2.4 (2021-08-05)
12
+
13
+ - Fixed memory leak
14
+
15
+ ## 0.2.3 (2021-03-14)
16
+
17
+ - Added ARM shared library for Linux
18
+
1
19
  ## 0.2.2 (2021-02-04)
2
20
 
3
21
  - Reduced allocations
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # LIBMF
1
+ # LIBMF Ruby
2
2
 
3
3
  [LIBMF](https://github.com/cjlin1/libmf) - large-scale sparse matrix factorization - for Ruby
4
4
 
5
5
  Check out [Disco](https://github.com/ankane/disco) for higher-level collaborative filtering
6
6
 
7
- [![Build Status](https://github.com/ankane/libmf/workflows/build/badge.svg?branch=master)](https://github.com/ankane/libmf/actions)
7
+ [![Build Status](https://github.com/ankane/libmf-ruby/workflows/build/badge.svg?branch=master)](https://github.com/ankane/libmf-ruby/actions)
8
8
 
9
9
  ## Installation
10
10
 
@@ -16,14 +16,13 @@ gem 'libmf'
16
16
 
17
17
  ## Getting Started
18
18
 
19
- Prep your data in the format `[row_index, column_index, value]`
19
+ Prep your data in the format `row_index, column_index, value`
20
20
 
21
21
  ```ruby
22
- data = [
23
- [0, 0, 5.0],
24
- [0, 2, 3.5],
25
- [1, 1, 4.0]
26
- ]
22
+ data = Libmf::Matrix.new
23
+ data.push(0, 0, 5.0)
24
+ data.push(0, 2, 3.5)
25
+ data.push(1, 1, 4.0)
27
26
  ```
28
27
 
29
28
  Create a model
@@ -90,7 +89,7 @@ Pass parameters - default values below
90
89
 
91
90
  ```ruby
92
91
  Libmf::Model.new(
93
- loss: 0, # loss function
92
+ loss: :real_l2, # loss function
94
93
  factors: 8, # number of latent factors
95
94
  threads: 12, # number of threads used
96
95
  bins: 25, # number of bins
@@ -111,21 +110,21 @@ Libmf::Model.new(
111
110
 
112
111
  For real-valued matrix factorization
113
112
 
114
- - 0 - squared error (L2-norm)
115
- - 1 - absolute error (L1-norm)
116
- - 2 - generalized KL-divergence
113
+ - `:real_l2` - squared error (L2-norm)
114
+ - `:real_l1` - absolute error (L1-norm)
115
+ - `:real_kl` - generalized KL-divergence
117
116
 
118
117
  For binary matrix factorization
119
118
 
120
- - 5 - logarithmic error
121
- - 6 - squared hinge loss
122
- - 7 - hinge loss
119
+ - `:binary_log` - logarithmic error
120
+ - `:binary_l2` - squared hinge loss
121
+ - `:binary_l1` - hinge loss
123
122
 
124
123
  For one-class matrix factorization
125
124
 
126
- - 10 - row-oriented pair-wise logarithmic loss
127
- - 11 - column-oriented pair-wise logarithmic loss
128
- - 12 - squared error (L2-norm)
125
+ - `:one_class_row` - row-oriented pair-wise logarithmic loss
126
+ - `:one_class_col` - column-oriented pair-wise logarithmic loss
127
+ - `:one_class_l2` - squared error (L2-norm)
129
128
 
130
129
  ## Performance
131
130
 
@@ -159,22 +158,22 @@ model.q_factors(format: :numo)
159
158
 
160
159
  ## History
161
160
 
162
- View the [changelog](https://github.com/ankane/libmf/blob/master/CHANGELOG.md)
161
+ View the [changelog](https://github.com/ankane/libmf-ruby/blob/master/CHANGELOG.md)
163
162
 
164
163
  ## Contributing
165
164
 
166
165
  Everyone is encouraged to help improve this project. Here are a few ways you can help:
167
166
 
168
- - [Report bugs](https://github.com/ankane/libmf/issues)
169
- - Fix bugs and [submit pull requests](https://github.com/ankane/libmf/pulls)
167
+ - [Report bugs](https://github.com/ankane/libmf-ruby/issues)
168
+ - Fix bugs and [submit pull requests](https://github.com/ankane/libmf-ruby/pulls)
170
169
  - Write, clarify, or fix documentation
171
170
  - Suggest or add new features
172
171
 
173
172
  To get started with development:
174
173
 
175
174
  ```sh
176
- git clone --recursive https://github.com/ankane/libmf.git
177
- cd libmf
175
+ git clone --recursive https://github.com/ankane/libmf-ruby.git
176
+ cd libmf-ruby
178
177
  bundle install
179
178
  bundle exec rake vendor:all
180
179
  bundle exec rake test
data/lib/libmf/ffi.rb CHANGED
@@ -43,15 +43,22 @@ module Libmf
43
43
  :b, :float,
44
44
  :p, :pointer,
45
45
  :q, :pointer
46
+
47
+ def self.release(pointer)
48
+ unless pointer.null?
49
+ ref = ::FFI::MemoryPointer.new(:pointer).write_pointer(pointer)
50
+ FFI.mf_destroy_model(ref)
51
+ end
52
+ end
46
53
  end
47
54
 
48
55
  attach_function :mf_get_default_param, [], Parameter.by_value
49
56
  attach_function :mf_read_problem, [:string], Problem.by_value
50
57
  attach_function :mf_save_model, [Model.by_ref, :string], :int
51
- attach_function :mf_load_model, [:string], Model.by_ref
52
- attach_function :mf_destroy_model, [Model.by_ref], :void
53
- attach_function :mf_train, [Problem.by_ref, Parameter.by_value], Model.by_ref
54
- attach_function :mf_train_with_validation, [Problem.by_ref, Problem.by_ref, Parameter.by_value], Model.by_ref
58
+ attach_function :mf_load_model, [:string], Model.auto_ptr
59
+ attach_function :mf_destroy_model, [:pointer], :void
60
+ attach_function :mf_train, [Problem.by_ref, Parameter.by_value], Model.auto_ptr
61
+ attach_function :mf_train_with_validation, [Problem.by_ref, Problem.by_ref, Parameter.by_value], Model.auto_ptr
55
62
  attach_function :mf_predict, [Model.by_ref, :int, :int], :float
56
63
  attach_function :mf_cross_validation, [Problem.by_ref, :int, Parameter.by_value], :double
57
64
  end
@@ -0,0 +1,13 @@
1
+ module Libmf
2
+ class Matrix
3
+ attr_reader :data
4
+
5
+ def initialize
6
+ @data = []
7
+ end
8
+
9
+ def push(row_index, column_index, value)
10
+ @data << [row_index, column_index, value]
11
+ end
12
+ end
13
+ end
data/lib/libmf/model.rb CHANGED
@@ -14,6 +14,7 @@ module Libmf
14
14
  else
15
15
  FFI.mf_train(train_set, param)
16
16
  end
17
+ raise Error, "fit failed" if @model.null?
17
18
 
18
19
  nil
19
20
  end
@@ -24,15 +25,20 @@ module Libmf
24
25
 
25
26
  def cv(data, folds: 5)
26
27
  problem = create_problem(data)
27
- FFI.mf_cross_validation(problem, folds, param)
28
+ # TODO update fork to differentiate between bad parameters and zero error
29
+ res = FFI.mf_cross_validation(problem, folds, param)
30
+ raise Error, "cv failed" if res == 0
31
+ res
28
32
  end
29
33
 
30
34
  def save_model(path)
31
- FFI.mf_save_model(model, path)
35
+ status = FFI.mf_save_model(model, path)
36
+ raise Error, "Cannot save model" if status != 0
32
37
  end
33
38
 
34
39
  def load_model(path)
35
40
  @model = FFI.mf_load_model(path)
41
+ raise Error, "Cannot open model" if @model.null?
36
42
  end
37
43
 
38
44
  def rows
@@ -80,6 +86,22 @@ module Libmf
80
86
  def param
81
87
  param = FFI.mf_get_default_param
82
88
  options = @options.dup
89
+
90
+ if options[:loss].is_a?(Symbol)
91
+ loss_map = {
92
+ real_l2: 0,
93
+ real_l1: 1,
94
+ real_kl: 2,
95
+ binary_log: 5,
96
+ binary_l2: 6,
97
+ binary_l1: 7,
98
+ one_class_row: 10,
99
+ one_class_col: 11,
100
+ one_class_l2: 12
101
+ }
102
+ options[:loss] = loss_map[options[:loss]] || (raise ArgumentError, "Unknown loss")
103
+ end
104
+
83
105
  # silence insufficient blocks warning with default params
84
106
  options[:bins] ||= 25 unless options[:nr_bins]
85
107
  options[:copy_data] = false unless options.key?(:copy_data)
@@ -107,6 +129,10 @@ module Libmf
107
129
  return FFI.mf_read_problem(File.expand_path(data))
108
130
  end
109
131
 
132
+ if data.is_a?(Matrix)
133
+ data = data.data
134
+ end
135
+
110
136
  raise Error, "No data" if data.empty?
111
137
 
112
138
  # TODO do in C for better performance
data/lib/libmf/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Libmf
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.6"
3
3
  end
data/lib/libmf.rb CHANGED
@@ -2,6 +2,7 @@
2
2
  require "ffi"
3
3
 
4
4
  # modules
5
+ require "libmf/matrix"
5
6
  require "libmf/model"
6
7
  require "libmf/version"
7
8
 
@@ -15,13 +16,17 @@ module Libmf
15
16
  if Gem.win_platform?
16
17
  "mf.dll"
17
18
  elsif RbConfig::CONFIG["host_os"] =~ /darwin/i
18
- if RbConfig::CONFIG["host_cpu"] =~ /arm/i
19
+ if RbConfig::CONFIG["host_cpu"] =~ /arm|aarch64/i
19
20
  "libmf.arm64.dylib"
20
21
  else
21
22
  "libmf.dylib"
22
23
  end
23
24
  else
24
- "libmf.so"
25
+ if RbConfig::CONFIG["host_cpu"] =~ /arm|aarch64/i
26
+ "libmf.arm64.so"
27
+ else
28
+ "libmf.so"
29
+ end
25
30
  end
26
31
  vendor_lib = File.expand_path("../vendor/#{lib_name}", __dir__)
27
32
  self.ffi_lib = [vendor_lib]
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libmf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-05 00:00:00.000000000 Z
11
+ date: 2021-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -35,16 +35,18 @@ files:
35
35
  - README.md
36
36
  - lib/libmf.rb
37
37
  - lib/libmf/ffi.rb
38
+ - lib/libmf/matrix.rb
38
39
  - lib/libmf/model.rb
39
40
  - lib/libmf/version.rb
40
41
  - vendor/COPYRIGHT
41
42
  - vendor/demo/real_matrix.te.txt
42
43
  - vendor/demo/real_matrix.tr.txt
43
44
  - vendor/libmf.arm64.dylib
45
+ - vendor/libmf.arm64.so
44
46
  - vendor/libmf.dylib
45
47
  - vendor/libmf.so
46
48
  - vendor/mf.dll
47
- homepage: https://github.com/ankane/libmf
49
+ homepage: https://github.com/ankane/libmf-ruby
48
50
  licenses:
49
51
  - BSD-3-Clause
50
52
  metadata: {}
@@ -63,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
65
  - !ruby/object:Gem::Version
64
66
  version: '0'
65
67
  requirements: []
66
- rubygems_version: 3.2.3
68
+ rubygems_version: 3.2.32
67
69
  signing_key:
68
70
  specification_version: 4
69
71
  summary: Large-scale sparse matrix factorization for Ruby