libmf 0.2.1 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d7744d5436815acc2609c8c39566f5704513248b5fe0b7ddc1cc4fbd28c81e9
4
- data.tar.gz: 0bcd219cc9360b0e9daefe418e2a14fc46969ab174db5ea93717c837e309c697
3
+ metadata.gz: 499cc67221be5e767caa7257e69c23a2b17c537c5950e2eeebb7de21d5bec1f0
4
+ data.tar.gz: 9345dc3804dfc928bbdb03b588e64f20fbb3ddb5a3674b65972c122842bef60e
5
5
  SHA512:
6
- metadata.gz: 49d31c33bea909e1a3a0a8cdebc6cce76b2cf19debc11420d76ad2016763961f376df56bdbba16e6a60e72a8014a2b44547045b2385f44b6c96f48f3f6148643
7
- data.tar.gz: 3f264d87a4cc727634ac8b2cb5dc87a56a44c3fba7d77093c83ca3623437be6024c04b1fe154275fd3a14a6736bf6703ea2fae9b984570b5c4629af3b5c710f3
6
+ metadata.gz: 6cd517a3e7eb418390012352a62de72aa9e3a4077b08c5d8602e89a9a8af0dcdc654a51359641e374d263804189a814afffa8e2f55db52ef7506e222e2143e5f
7
+ data.tar.gz: 5dad62dd22cd82499d4f0c2b2c06792945c50065a2517d89baeec8d00c62e2628d62d3702cc3964344c3e437f4281afd1f79f58291efca12f3c788e5ed07d0e1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## 0.2.5 (2021-10-18)
2
+
3
+ - Added named loss functions
4
+ - Added `Matrix` class
5
+ - Improved error checking for `fit`, `cv`, `save_model`, and `load_model`
6
+
7
+ ## 0.2.4 (2021-08-05)
8
+
9
+ - Fixed memory leak
10
+
11
+ ## 0.2.3 (2021-03-14)
12
+
13
+ - Added ARM shared library for Linux
14
+
15
+ ## 0.2.2 (2021-02-04)
16
+
17
+ - Reduced allocations
18
+ - Improved ARM detection
19
+
1
20
  ## 0.2.1 (2020-12-28)
2
21
 
3
22
  - Added ARM shared library for Mac
data/LICENSE.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  BSD 3-Clause License
2
2
 
3
3
  Copyright (c) 2014-2015, The LIBMF Project
4
- Copyright (c) 2019-2020, Andrew Kane
4
+ Copyright (c) 2019-2021, Andrew Kane
5
5
  All rights reserved.
6
6
 
7
7
  Redistribution and use in source and binary forms, with or without
data/README.md CHANGED
@@ -90,7 +90,7 @@ Pass parameters - default values below
90
90
 
91
91
  ```ruby
92
92
  Libmf::Model.new(
93
- loss: 0, # loss function
93
+ loss: :real_l2, # loss function
94
94
  factors: 8, # number of latent factors
95
95
  threads: 12, # number of threads used
96
96
  bins: 25, # number of bins
@@ -111,21 +111,21 @@ Libmf::Model.new(
111
111
 
112
112
  For real-valued matrix factorization
113
113
 
114
- - 0 - squared error (L2-norm)
115
- - 1 - absolute error (L1-norm)
116
- - 2 - generalized KL-divergence
114
+ - `:real_l2` - squared error (L2-norm)
115
+ - `:real_l1` - absolute error (L1-norm)
116
+ - `:real_kl` - generalized KL-divergence
117
117
 
118
118
  For binary matrix factorization
119
119
 
120
- - 5 - logarithmic error
121
- - 6 - squared hinge loss
122
- - 7 - hinge loss
120
+ - `:binary_log` - logarithmic error
121
+ - `:binary_l2` - squared hinge loss
122
+ - `:binary_l1` - hinge loss
123
123
 
124
124
  For one-class matrix factorization
125
125
 
126
- - 10 - row-oriented pair-wise logarithmic loss
127
- - 11 - column-oriented pair-wise logarithmic loss
128
- - 12 - squared error (L2-norm)
126
+ - `:one_class_row` - row-oriented pair-wise logarithmic loss
127
+ - `:one_class_col` - column-oriented pair-wise logarithmic loss
128
+ - `:one_class_l2` - squared error (L2-norm)
129
129
 
130
130
  ## Performance
131
131
 
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,14 +129,19 @@ 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
113
139
  # can use FIX2INT() and RFLOAT_VALUE() instead of pack
140
+ # and write directly to C string
114
141
  buffer = String.new
142
+ pack_format = "iif"
115
143
  data.each do |row|
116
- row[0, 2].pack("i*".freeze, buffer: buffer)
117
- row[2, 1].pack("f".freeze, buffer: buffer)
144
+ row.pack(pack_format, buffer: buffer)
118
145
  end
119
146
 
120
147
  r = ::FFI::MemoryPointer.new(FFI::Node, data.size)
data/lib/libmf/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Libmf
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.5"
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
 
@@ -14,12 +15,18 @@ module Libmf
14
15
  lib_name =
15
16
  if Gem.win_platform?
16
17
  "mf.dll"
17
- elsif RbConfig::CONFIG["arch"] =~ /arm64-darwin/i
18
- "libmf.arm64.dylib"
19
18
  elsif RbConfig::CONFIG["host_os"] =~ /darwin/i
20
- "libmf.dylib"
19
+ if RbConfig::CONFIG["host_cpu"] =~ /arm/i
20
+ "libmf.arm64.dylib"
21
+ else
22
+ "libmf.dylib"
23
+ end
21
24
  else
22
- "libmf.so"
25
+ if RbConfig::CONFIG["host_cpu"] =~ /aarch64/i
26
+ "libmf.arm64.so"
27
+ else
28
+ "libmf.so"
29
+ end
23
30
  end
24
31
  vendor_lib = File.expand_path("../vendor/#{lib_name}", __dir__)
25
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.1
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-29 00:00:00.000000000 Z
11
+ date: 2021-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  description:
28
- email: andrew@chartkick.com
28
+ email: andrew@ankane.org
29
29
  executables: []
30
30
  extensions: []
31
31
  extra_rdoc_files: []
@@ -35,12 +35,14 @@ 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
@@ -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.22
67
69
  signing_key:
68
70
  specification_version: 4
69
71
  summary: Large-scale sparse matrix factorization for Ruby