cmfrec 0.1.6 → 0.2.1

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: 34e8dc08914cbc418470cd7eb3adf3d33013b786319f4510212c80bf3629f3ca
4
- data.tar.gz: c1b91a1f77b4b51a5ca4491376f8a02230ea54873f8c1b2b06f4761d6ddd0686
3
+ metadata.gz: 3bd946bc2c7425ba3550a9cd5bf346b0cac6de5597d0d38ff0dfb49db32e754d
4
+ data.tar.gz: 20038dc0c401389d75dc3a35415919f6721dab7e32459eeb34c9dcb23569c49a
5
5
  SHA512:
6
- metadata.gz: a3c57734379199196a4e3f51d9ec02b19ef1abac13d57a10ca3c20e9b76c9ee5db4b17d790330d41a9576c2ba28a9eeccafeb5760b54cfdf80a7431368895068
7
- data.tar.gz: 5a24a77a6665854abb38916a22e8141a6cae637a51f98e3df3762566f2e73cb60b9bd9a25303df0411ea53dec3211bf7534711dc55c510311620341cbe4e4ac3
6
+ metadata.gz: 1d0019e89fe0ca946cd83d60c052bff2e3dcc3990e3bf37de92b620870b6bf8a31010aec87bb2e5a352c446441b683f0148da83497b5d44ef89d62396f4b7d3c
7
+ data.tar.gz: f4d65f294b9a313c2c86111eaf66e734999924392fedc4715c51919a4ca4de3864ba3eec732f3d707dd06ea62ddb931d9b28b8436a09a807c01e14ac2c542e44
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## 0.2.1 (2022-07-11)
2
+
3
+ - Added support for JSON serialization
4
+
5
+ ## 0.2.0 (2022-06-14)
6
+
7
+ - Updated cmfrec to 3.4.2
8
+ - Fixed missing item ids with `load_movielens`
9
+ - Dropped support for Ruby < 2.7
10
+
11
+ ## 0.1.7 (2022-03-22)
12
+
13
+ - Improved ARM detection
14
+ - Fixed error with `load_movielens`
15
+ - Fixed duplicates in `item_info` with `load_movielens`
16
+
1
17
  ## 0.1.6 (2021-08-12)
2
18
 
3
19
  - Added `user_ids` and `item_ids` methods
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # cmfrec
1
+ # cmfrec Ruby
2
2
 
3
3
  :fire: Recommendations for Ruby, powered by [cmfrec](https://github.com/david-cortes/cmfrec)
4
4
 
@@ -6,14 +6,14 @@
6
6
  - Works with explicit and implicit feedback
7
7
  - Uses high-performance matrix factorization
8
8
 
9
- [![Build Status](https://github.com/ankane/cmfrec/workflows/build/badge.svg?branch=master)](https://github.com/ankane/cmfrec/actions)
9
+ [![Build Status](https://github.com/ankane/cmfrec-ruby/workflows/build/badge.svg?branch=master)](https://github.com/ankane/cmfrec-ruby/actions)
10
10
 
11
11
  ## Installation
12
12
 
13
13
  Add this line to your application’s Gemfile:
14
14
 
15
15
  ```ruby
16
- gem 'cmfrec'
16
+ gem "cmfrec"
17
17
  ```
18
18
 
19
19
  For Windows, also follow [these instructions](#windows-installation).
@@ -58,8 +58,8 @@ Get recommendations for a new user
58
58
 
59
59
  ```ruby
60
60
  recommender.new_user_recs([
61
- {item_id: 1, value: 5},
62
- {item_id: 2, value: 3}
61
+ {item_id: 1, rating: 5},
62
+ {item_id: 2, rating: 3}
63
63
  ])
64
64
  ```
65
65
 
@@ -82,11 +82,11 @@ Add side information about users, items, or both
82
82
  ```ruby
83
83
  user_info = [
84
84
  {user_id: 1, cats: 1, dogs: 0},
85
- {user_id: 2, cats: 2, dogs: 1},
85
+ {user_id: 2, cats: 2, dogs: 1}
86
86
  ]
87
87
  item_info = [
88
88
  {item_id: 1, genre_comedy: 1, genre_drama: 0},
89
- {item_id: 2, genre_comedy: 0, genre_drama: 1},
89
+ {item_id: 2, genre_comedy: 0, genre_drama: 1}
90
90
  ]
91
91
  recommender.fit(ratings, user_info: user_info, item_info: item_info)
92
92
  ```
@@ -112,7 +112,7 @@ recommender.new_user_recs([], user_info: {cats: 0, dogs: 2})
112
112
  Add this line to your application’s Gemfile:
113
113
 
114
114
  ```ruby
115
- gem 'ngt'
115
+ gem "ngt"
116
116
  ```
117
117
 
118
118
  Get similar users
@@ -150,11 +150,7 @@ recommender.predict(ratings.last(20000))
150
150
  [Ahoy](https://github.com/ankane/ahoy) is a great source for implicit feedback
151
151
 
152
152
  ```ruby
153
- views = Ahoy::Event.
154
- where(name: "Viewed post").
155
- group(:user_id).
156
- group("properties->>'post_id'"). # postgres syntax
157
- count
153
+ views = Ahoy::Event.where(name: "Viewed post").group(:user_id).group_prop(:post_id).count
158
154
 
159
155
  data =
160
156
  views.map do |(user_id, post_id), count|
@@ -217,19 +213,21 @@ Rover.read_csv("ratings.csv")
217
213
  Store the recommender
218
214
 
219
215
  ```ruby
220
- bin = Marshal.dump(recommender)
221
- File.binwrite("recommender.bin", bin)
216
+ json = recommender.to_json
217
+ File.write("recommender.json", json)
222
218
  ```
223
219
 
224
- > You can save it to a file, database, or any other storage system
220
+ The serialized recommender includes user activity from the training data (to avoid recommending previously rated items), so be sure to protect it. You can save it to a file, database, or any other storage system, or use a tool like [Trove](https://github.com/ankane/trove). Also, user and item IDs should be integers or strings for this.
225
221
 
226
222
  Load a recommender
227
223
 
228
224
  ```ruby
229
- bin = File.binread("recommender.bin")
230
- recommender = Marshal.load(bin)
225
+ json = File.read("recommender.json")
226
+ recommender = Cmfrec::Recommender.load_json(json)
231
227
  ```
232
228
 
229
+ Alternatively, you can store only the factors and use a library like [Neighbor](https://github.com/ankane/neighbor). See the [examples](https://github.com/ankane/neighbor/tree/master/examples) for Disco, which has a similar API. For explicit feedback, you should [disable the bias](#explicit-feedback) with this approach.
230
+
233
231
  ## Reference
234
232
 
235
233
  Get ids
@@ -269,22 +267,22 @@ Cmfrec.ffi_lib = "path/to/cmfrec.dll"
269
267
 
270
268
  ## History
271
269
 
272
- View the [changelog](https://github.com/ankane/cmfrec/blob/master/CHANGELOG.md)
270
+ View the [changelog](https://github.com/ankane/cmfrec-ruby/blob/master/CHANGELOG.md)
273
271
 
274
272
  ## Contributing
275
273
 
276
274
  Everyone is encouraged to help improve this project. Here are a few ways you can help:
277
275
 
278
- - [Report bugs](https://github.com/ankane/cmfrec/issues)
279
- - Fix bugs and [submit pull requests](https://github.com/ankane/cmfrec/pulls)
276
+ - [Report bugs](https://github.com/ankane/cmfrec-ruby/issues)
277
+ - Fix bugs and [submit pull requests](https://github.com/ankane/cmfrec-ruby/pulls)
280
278
  - Write, clarify, or fix documentation
281
279
  - Suggest or add new features
282
280
 
283
281
  To get started with development:
284
282
 
285
283
  ```sh
286
- git clone https://github.com/ankane/cmfrec.git
287
- cd cmfrec
284
+ git clone https://github.com/ankane/cmfrec-ruby.git
285
+ cd cmfrec-ruby
288
286
  bundle install
289
287
  bundle exec rake vendor:all
290
288
  bundle exec rake test
data/lib/cmfrec/data.rb CHANGED
@@ -3,11 +3,11 @@ module Cmfrec
3
3
  def load_movielens
4
4
  require "csv"
5
5
 
6
- data_path = download_file("ml-100k/u.data", "http://files.grouplens.org/datasets/movielens/ml-100k/u.data",
6
+ data_path = download_file("ml-100k/u.data", "https://files.grouplens.org/datasets/movielens/ml-100k/u.data",
7
7
  file_hash: "06416e597f82b7342361e41163890c81036900f418ad91315590814211dca490")
8
- user_path = download_file("ml-100k/u.user", "http://files.grouplens.org/datasets/movielens/ml-100k/u.user",
8
+ user_path = download_file("ml-100k/u.user", "https://files.grouplens.org/datasets/movielens/ml-100k/u.user",
9
9
  file_hash: "f120e114da2e8cf314fd28f99417c94ae9ddf1cb6db8ce0e4b5995d40e90e62c")
10
- item_path = download_file("ml-100k/u.item", "http://files.grouplens.org/datasets/movielens/ml-100k/u.item",
10
+ item_path = download_file("ml-100k/u.item", "https://files.grouplens.org/datasets/movielens/ml-100k/u.item",
11
11
  file_hash: "553841ebc7de3a0fd0d6b62a204ea30c1e651aacfb2814c7a6584ac52f2c5701")
12
12
 
13
13
  # convert u.item to utf-8
@@ -24,9 +24,15 @@ module Cmfrec
24
24
 
25
25
  item_info = []
26
26
  movies = {}
27
+ movie_names = {}
27
28
  genres = %w(unknown action adventure animation childrens comedy crime documentary drama fantasy filmnoir horror musical mystery romance scifi thriller war western)
28
29
  CSV.parse(movies_str, col_sep: "|", converters: [:numeric]) do |row|
29
30
  movies[row[0]] = row[1]
31
+
32
+ # filter duplicates
33
+ next if movie_names[row[1]]
34
+ movie_names[row[1]] = true
35
+
30
36
  item = {item_id: row[1], year: row[2] ? Date.parse(row[2]).year : 1970}
31
37
  genres.each_with_index do |genre, i|
32
38
  item[:"genre_#{genre}"] = row[i + 5]
@@ -49,7 +55,10 @@ module Cmfrec
49
55
  private
50
56
 
51
57
  def download_file(fname, origin, file_hash:)
58
+ require "digest"
52
59
  require "fileutils"
60
+ require "net/http"
61
+ require "tmpdir"
53
62
 
54
63
  # TODO handle this better
55
64
  raise "No HOME" unless ENV["HOME"]
@@ -58,10 +67,6 @@ module Cmfrec
58
67
 
59
68
  return dest if File.exist?(dest)
60
69
 
61
- require "digest"
62
- require "net/http"
63
- require "tmpdir"
64
-
65
70
  temp_path = "#{Dir.tmpdir}/cmfrec-#{Time.now.to_f}" # TODO better name
66
71
 
67
72
  digest = Digest::SHA2.new
data/lib/cmfrec/ffi.rb CHANGED
@@ -17,12 +17,13 @@ module Cmfrec
17
17
  typealias "int_t", "int"
18
18
  typealias "real_t", "double"
19
19
 
20
- extern "int_t fit_collective_explicit_als(real_t *restrict biasA, real_t *restrict biasB, real_t *restrict A, real_t *restrict B, real_t *restrict C, real_t *restrict D, real_t *restrict Ai, real_t *restrict Bi, bool add_implicit_features, bool reset_values, int_t seed, real_t *restrict glob_mean, real_t *restrict U_colmeans, real_t *restrict I_colmeans, int_t m, int_t n, int_t k, int_t ixA[], int_t ixB[], real_t *restrict X, size_t nnz, real_t *restrict Xfull, real_t *restrict weight, bool user_bias, bool item_bias, bool center, real_t lam, real_t *restrict lam_unique, real_t l1_lam, real_t *restrict l1_lam_unique, bool scale_lam, bool scale_lam_sideinfo, real_t *restrict U, int_t m_u, int_t p, real_t *restrict II, int_t n_i, int_t q, int_t U_row[], int_t U_col[], real_t *restrict U_sp, size_t nnz_U, int_t I_row[], int_t I_col[], real_t *restrict I_sp, size_t nnz_I, bool NA_as_zero_X, bool NA_as_zero_U, bool NA_as_zero_I, int_t k_main, int_t k_user, int_t k_item, real_t w_main, real_t w_user, real_t w_item, real_t w_implicit, int_t niter, int_t nthreads, bool verbose, bool handle_interrupt, bool use_cg, int_t max_cg_steps, bool finalize_chol, bool nonneg, int_t max_cd_steps, bool nonneg_C, bool nonneg_D, bool precompute_for_predictions, bool include_all_X, real_t *restrict B_plus_bias, real_t *restrict precomputedBtB, real_t *restrict precomputedTransBtBinvBt, real_t *restrict precomputedBtXbias, real_t *restrict precomputedBeTBeChol, real_t *restrict precomputedBiTBi, real_t *restrict precomputedTransCtCinvCt, real_t *restrict precomputedCtCw)"
21
- extern "int_t fit_collective_implicit_als(real_t *restrict A, real_t *restrict B, real_t *restrict C, real_t *restrict D, bool reset_values, int_t seed, real_t *restrict U_colmeans, real_t *restrict I_colmeans, int_t m, int_t n, int_t k, int_t ixA[], int_t ixB[], real_t *restrict X, size_t nnz, real_t lam, real_t *restrict lam_unique, real_t l1_lam, real_t *restrict l1_lam_unique, real_t *restrict U, int_t m_u, int_t p, real_t *restrict II, int_t n_i, int_t q, int_t U_row[], int_t U_col[], real_t *restrict U_sp, size_t nnz_U, int_t I_row[], int_t I_col[], real_t *restrict I_sp, size_t nnz_I, bool NA_as_zero_U, bool NA_as_zero_I, int_t k_main, int_t k_user, int_t k_item, real_t w_main, real_t w_user, real_t w_item, real_t *restrict w_main_multiplier, real_t alpha, bool adjust_weight, bool apply_log_transf, int_t niter, int_t nthreads, bool verbose, bool handle_interrupt, bool use_cg, int_t max_cg_steps, bool finalize_chol, bool nonneg, int_t max_cd_steps, bool nonneg_C, bool nonneg_D, bool precompute_for_predictions, real_t *restrict precomputedBtB, real_t *restrict precomputedBeTBe, real_t *restrict precomputedBeTBeChol)"
22
- extern "int_t factors_collective_explicit_single(real_t *restrict a_vec, real_t *restrict a_bias,real_t *restrict u_vec, int_t p,real_t *restrict u_vec_sp, int_t u_vec_ixB[], size_t nnz_u_vec,real_t *restrict u_bin_vec, int_t pbin,bool NA_as_zero_U, bool NA_as_zero_X,bool nonneg,real_t *restrict C, real_t *restrict Cb,real_t glob_mean, real_t *restrict biasB,real_t *restrict U_colmeans,real_t *restrict Xa, int_t ixB[], size_t nnz,real_t *restrict Xa_dense, int_t n,real_t *restrict weight,real_t *restrict B,real_t *restrict Bi, bool add_implicit_features,int_t k, int_t k_user, int_t k_item, int_t k_main,real_t lam, real_t *restrict lam_unique,real_t l1_lam, real_t *restrict l1_lam_unique,bool scale_lam, bool scale_lam_sideinfo,real_t w_main, real_t w_user, real_t w_implicit,int_t n_max, bool include_all_X,real_t *restrict BtB,real_t *restrict TransBtBinvBt,real_t *restrict BtXbias,real_t *restrict BeTBeChol,real_t *restrict BiTBi,real_t *restrict CtCw,real_t *restrict TransCtCinvCt,real_t *restrict B_plus_bias)"
23
- extern "int_t factors_collective_implicit_single(real_t *restrict a_vec,real_t *restrict u_vec, int_t p,real_t *restrict u_vec_sp, int_t u_vec_ixB[], size_t nnz_u_vec,bool NA_as_zero_U,bool nonneg,real_t *restrict U_colmeans,real_t *restrict B, int_t n, real_t *restrict C,real_t *restrict Xa, int_t ixB[], size_t nnz,int_t k, int_t k_user, int_t k_item, int_t k_main,real_t lam, real_t l1_lam, real_t alpha, real_t w_main, real_t w_user,real_t w_main_multiplier,bool apply_log_transf,real_t *restrict BeTBe,real_t *restrict BtB,real_t *restrict BeTBeChol)"
24
- extern "int_t predict_X_old_collective_explicit(int_t row[], int_t col[], real_t *restrict predicted, size_t n_predict, real_t *restrict A, real_t *restrict biasA, real_t *restrict B, real_t *restrict biasB, real_t glob_mean, int_t k, int_t k_user, int_t k_item, int_t k_main, int_t m, int_t n_max, int_t nthreads)"
25
- extern "int_t predict_X_old_collective_implicit(int_t row[], int_t col[], real_t *restrict predicted, size_t n_predict, real_t *restrict A, real_t *restrict B, int_t k, int_t k_user, int_t k_item, int_t k_main, int_t m, int_t n, int_t nthreads)"
26
- extern "int_t topN(real_t *restrict a_vec, int_t k_user, real_t *restrict B, int_t k_item, real_t *restrict biasB, real_t glob_mean, real_t biasA, int_t k, int_t k_main, int_t *restrict include_ix, int_t n_include, int_t *restrict exclude_ix, int_t n_exclude, int_t *restrict outp_ix, real_t *restrict outp_score, int_t n_top, int_t n, int_t nthreads)"
20
+ extern "int_t fit_collective_explicit_als(real_t *restrict biasA, real_t *restrict biasB, real_t *restrict A, real_t *restrict B, real_t *restrict C, real_t *restrict D, real_t *restrict Ai, real_t *restrict Bi, bool add_implicit_features, bool reset_values, int_t seed, real_t *restrict glob_mean, real_t *restrict U_colmeans, real_t *restrict I_colmeans, int_t m, int_t n, int_t k, int_t ixA[], int_t ixB[], real_t *restrict X, size_t nnz, real_t *restrict Xfull, real_t *restrict weight, bool user_bias, bool item_bias, bool center, real_t lam, real_t *restrict lam_unique, real_t l1_lam, real_t *restrict l1_lam_unique, bool scale_lam, bool scale_lam_sideinfo, bool scale_bias_const, real_t *scaling_biasA, real_t *scaling_biasB, real_t *restrict U, int_t m_u, int_t p, real_t *restrict II, int_t n_i, int_t q, int_t U_row[], int_t U_col[], real_t *restrict U_sp, size_t nnz_U, int_t I_row[], int_t I_col[], real_t *restrict I_sp, size_t nnz_I, bool NA_as_zero_X, bool NA_as_zero_U, bool NA_as_zero_I, int_t k_main, int_t k_user, int_t k_item, real_t w_main, real_t w_user, real_t w_item, real_t w_implicit, int_t niter, int nthreads, bool verbose, bool handle_interrupt, bool use_cg, int_t max_cg_steps, bool precondition_cg, bool finalize_chol, bool nonneg, int_t max_cd_steps, bool nonneg_C, bool nonneg_D, bool precompute_for_predictions, bool include_all_X, real_t *restrict B_plus_bias, real_t *restrict precomputedBtB, real_t *restrict precomputedTransBtBinvBt, real_t *restrict precomputedBtXbias, real_t *restrict precomputedBeTBeChol, real_t *restrict precomputedBiTBi, real_t *restrict precomputedTransCtCinvCt, real_t *restrict precomputedCtCw, real_t *precomputedCtUbias)"
21
+ extern "int_t fit_collective_implicit_als(real_t *restrict A, real_t *restrict B, real_t *restrict C, real_t *restrict D, bool reset_values, int_t seed, real_t *restrict U_colmeans, real_t *restrict I_colmeans, int_t m, int_t n, int_t k, int_t ixA[], int_t ixB[], real_t *restrict X, size_t nnz, real_t lam, real_t *restrict lam_unique, real_t l1_lam, real_t *restrict l1_lam_unique, real_t *restrict U, int_t m_u, int_t p, real_t *restrict II, int_t n_i, int_t q, int_t U_row[], int_t U_col[], real_t *restrict U_sp, size_t nnz_U, int_t I_row[], int_t I_col[], real_t *restrict I_sp, size_t nnz_I, bool NA_as_zero_U, bool NA_as_zero_I, int_t k_main, int_t k_user, int_t k_item, real_t w_main, real_t w_user, real_t w_item, real_t *restrict w_main_multiplier, real_t alpha, bool adjust_weight, bool apply_log_transf, int_t niter, int nthreads, bool verbose, bool handle_interrupt, bool use_cg, int_t max_cg_steps, bool precondition_cg, bool finalize_chol, bool nonneg, int_t max_cd_steps, bool nonneg_C, bool nonneg_D, bool precompute_for_predictions, real_t *restrict precomputedBtB, real_t *restrict precomputedBeTBe, real_t *restrict precomputedBeTBeChol, real_t *precomputedCtUbias)"
22
+ extern "int_t predict_X_old_collective_explicit(int_t row[], int_t col[], real_t *restrict predicted, size_t n_predict, real_t *restrict A, real_t *restrict biasA, real_t *restrict B, real_t *restrict biasB, real_t glob_mean, int_t k, int_t k_user, int_t k_item, int_t k_main, int_t m, int_t n_max, int nthreads)"
23
+ extern "int_t predict_X_old_collective_implicit(int_t row[], int_t col[], real_t *restrict predicted, size_t n_predict, real_t *restrict A, real_t *restrict B, int_t k, int_t k_user, int_t k_item, int_t k_main, int_t m, int_t n, int nthreads)"
24
+ extern "int_t topN_old_collective_explicit(real_t *a_vec, real_t a_bias, real_t *A, real_t *biasA, int_t row_index, real_t *B, real_t *biasB, real_t glob_mean, int_t k, int_t k_user, int_t k_item, int_t k_main, int_t *include_ix, int_t n_include, int_t *exclude_ix, int_t n_exclude, int_t *outp_ix, real_t *outp_score, int_t n_top, int_t n, int_t n_max, bool include_all_X, int nthreads)"
25
+ extern "int_t topN_old_collective_implicit(real_t *a_vec, real_t *A, int_t row_index, real_t *B, int_t k, int_t k_user, int_t k_item, int_t k_main, int_t *include_ix, int_t n_include, int_t *exclude_ix, int_t n_exclude, int_t *outp_ix, real_t *outp_score, int_t n_top, int_t n, int nthreads)"
26
+ extern "int_t topN_new_collective_explicit(bool user_bias, real_t *u_vec, int_t p, real_t *u_vec_sp, int_t u_vec_X_col[], size_t nnz_u_vec, real_t *u_bin_vec, int_t pbin, bool NA_as_zero_U, bool NA_as_zero_X, bool nonneg, real_t *C, real_t *Cb, real_t glob_mean, real_t *biasB, real_t *U_colmeans, real_t *Xa, int_t X_col[], size_t nnz, real_t *Xa_dense, int_t n, real_t *weight, real_t *B, real_t *Bi, bool add_implicit_features, int_t k, int_t k_user, int_t k_item, int_t k_main, real_t lam, real_t *lam_unique, real_t l1_lam, real_t *l1_lam_unique, bool scale_lam, bool scale_lam_sideinfo, bool scale_bias_const, real_t scaling_biasA, real_t w_main, real_t w_user, real_t w_implicit, int_t n_max, bool include_all_X, real_t *BtB, real_t *TransBtBinvBt, real_t *BtXbias, real_t *BeTBeChol, real_t *BiTBi, real_t *CtCw, real_t *TransCtCinvCt, real_t *CtUbias, real_t *B_plus_bias, int_t *include_ix, int_t n_include, int_t *exclude_ix, int_t n_exclude, int_t *outp_ix, real_t *outp_score, int_t n_top, int nthreads)"
27
+ extern "int_t topN_new_collective_implicit(int_t n, real_t *u_vec, int_t p, real_t *u_vec_sp, int_t u_vec_X_col[], size_t nnz_u_vec, bool NA_as_zero_U, bool nonneg, real_t *U_colmeans, real_t *B, real_t *C, real_t *Xa, int_t X_col[], size_t nnz, int_t k, int_t k_user, int_t k_item, int_t k_main, real_t lam, real_t l1_lam, real_t alpha, real_t w_main, real_t w_user, real_t w_main_multiplier, bool apply_log_transf, real_t *BeTBe, real_t *BtB, real_t *BeTBeChol, real_t *CtUbias, int_t *include_ix, int_t n_include, int_t *exclude_ix, int_t n_exclude, int_t *outp_ix, real_t *outp_score, int_t n_top, int nthreads)"
27
28
  end
28
29
  end
@@ -70,7 +70,7 @@ module Cmfrec
70
70
  a_vec = @a[user * @k * Fiddle::SIZEOF_DOUBLE, @k * Fiddle::SIZEOF_DOUBLE]
71
71
  a_bias = @bias_a ? @bias_a[user * Fiddle::SIZEOF_DOUBLE, Fiddle::SIZEOF_DOUBLE].unpack1("d") : 0
72
72
  # @rated[user] will be nil for recommenders saved before 0.1.5
73
- top_n(a_vec: a_vec, a_bias: a_bias, count: count, rated: (@rated[user] || {}).keys, item_ids: item_ids)
73
+ top_n(a_vec: a_vec, a_bias: a_bias, count: count, rated: (@rated[user] || {}).keys, item_ids: item_ids, row_index: user)
74
74
  else
75
75
  # no items if user is unknown
76
76
  # TODO maybe most popular items
@@ -81,8 +81,137 @@ module Cmfrec
81
81
  def new_user_recs(data, count: 5, user_info: nil, item_ids: nil)
82
82
  check_fit
83
83
 
84
- a_vec, a_bias, rated = factors_warm(data, user_info: user_info)
85
- top_n(a_vec: a_vec, a_bias: a_bias, count: count, rated: rated, item_ids: item_ids)
84
+ data = to_dataset(data)
85
+ user_info = to_dataset(user_info) if user_info
86
+
87
+ # remove unknown items
88
+ data, unknown_data = data.partition { |d| @item_map[d[:item_id]] }
89
+
90
+ if unknown_data.any?
91
+ # TODO warn for unknown items?
92
+ # warn "[cmfrec] Unknown items: #{unknown_data.map { |d| d[:item_id] }.join(", ")}"
93
+ end
94
+
95
+ rated_ids = data.map { |d| @item_map[d[:item_id]] }
96
+
97
+ nnz = data.size
98
+
99
+ u_vec_sp = []
100
+ u_vec_x_col = []
101
+ if user_info
102
+ user_info.each do |k, v|
103
+ next if k == :user_id
104
+
105
+ uc = @user_info_map[k]
106
+ raise "Bad key: #{k}" unless uc
107
+
108
+ u_vec_x_col << uc
109
+ u_vec_sp << v
110
+ end
111
+ end
112
+ p_ = @user_info_map.size
113
+ nnz_u_vec = u_vec_sp.size
114
+ u_vec_x_col = int_ptr(u_vec_x_col)
115
+ u_vec_sp = real_ptr(u_vec_sp)
116
+
117
+ u_vec = nil
118
+ u_bin_vec = nil
119
+ pbin = 0
120
+
121
+ weight = nil
122
+ lam_unique = nil
123
+ l1_lam_unique = nil
124
+ n_max = @n
125
+
126
+ if data.any?
127
+ if @implicit
128
+ ratings = data.map { |d| d[:value] || 1 }
129
+ else
130
+ ratings = data.map { |d| d[:rating] }
131
+ check_ratings(ratings)
132
+ end
133
+ xa = real_ptr(ratings)
134
+ x_col = int_ptr(rated_ids)
135
+ else
136
+ xa = nil
137
+ x_col = nil
138
+ end
139
+ xa_dense = nil
140
+
141
+ rated = rated_ids.uniq
142
+
143
+ prep = prepare_top_n(count: count, rated: rated, item_ids: item_ids)
144
+ return [] if prep.empty?
145
+ include_ix, n_include, exclude_ix, n_exclude, outp_ix, outp_score, count = prep
146
+
147
+ if @implicit
148
+ args = [
149
+ @n,
150
+ u_vec, p_,
151
+ u_vec_sp, u_vec_x_col, nnz_u_vec,
152
+ @na_as_zero_user,
153
+ @nonneg,
154
+ @u_colmeans,
155
+ @b, @c,
156
+ xa, x_col, nnz,
157
+ @k, @k_user, @k_item, @k_main,
158
+ @lambda_, @l1_lambda, @alpha, @w_main, @w_user,
159
+ @w_main_multiplier,
160
+ @apply_log_transf,
161
+ nil, #BeTBe,
162
+ nil, #BtB,
163
+ nil, #BeTBeChol,
164
+ nil, #CtUbias,
165
+ include_ix, n_include,
166
+ exclude_ix, n_exclude,
167
+ outp_ix, outp_score,
168
+ count, @nthreads
169
+ ]
170
+ check_status FFI.topN_new_collective_implicit(*fiddle_args(args))
171
+ else
172
+ cb = nil
173
+ scaling_bias_a = 0
174
+
175
+ args = [
176
+ @user_bias,
177
+ u_vec, p_,
178
+ u_vec_sp, u_vec_x_col, nnz_u_vec,
179
+ u_bin_vec, pbin,
180
+ @na_as_zero_user, @na_as_zero,
181
+ @nonneg,
182
+ @c, cb,
183
+ @global_mean, @bias_b,
184
+ @u_colmeans,
185
+ xa, x_col, nnz,
186
+ xa_dense, @n,
187
+ weight,
188
+ @b,
189
+ @bi, @add_implicit_features,
190
+ @k, @k_user, @k_item, @k_main,
191
+ @lambda_, lam_unique,
192
+ @l1_lambda, l1_lam_unique,
193
+ @scale_lam, @scale_lam_sideinfo,
194
+ @scale_bias_const, scaling_bias_a,
195
+ @w_main, @w_user, @w_implicit,
196
+ n_max, @include_all_x,
197
+ nil, #BtB,
198
+ nil, #TransBtBinvBt,
199
+ nil, #BtXbias,
200
+ nil, #BeTBeChol,
201
+ nil, #BiTBi,
202
+ nil, #CtCw,
203
+ nil, #TransCtCinvCt,
204
+ nil, #CtUbias,
205
+ nil, #B_plus_bias,
206
+ include_ix, n_include,
207
+ exclude_ix, n_exclude,
208
+ outp_ix, outp_score,
209
+ count, @nthreads
210
+ ]
211
+ check_status FFI.topN_new_collective_explicit(*fiddle_args(args))
212
+ end
213
+
214
+ top_n_output(outp_ix, outp_score)
86
215
  end
87
216
 
88
217
  def user_ids
@@ -120,6 +249,68 @@ module Cmfrec
120
249
  similar(user_id, @user_map, user_factors, count, user_index)
121
250
  end
122
251
 
252
+ def to_json
253
+ require "base64"
254
+ require "json"
255
+
256
+ obj = {
257
+ implicit: @implicit
258
+ }
259
+
260
+ # options
261
+ obj[:factors] = @k
262
+ obj[:epochs] = @niter
263
+ obj[:verbose] = @verbose
264
+
265
+ # factors
266
+ obj[:user_ids] = @user_map.keys
267
+ obj[:item_ids] = @item_map.keys
268
+ obj[:rated] = @user_map.map { |_, u| (@rated[u] || {}).keys }
269
+ obj[:user_factors] = json_dump_ptr(@a)
270
+ obj[:item_factors] = json_dump_ptr(@b)
271
+
272
+ # bias
273
+ obj[:user_bias] = json_dump_ptr(@bias_a)
274
+ obj[:item_bias] = json_dump_ptr(@bias_b)
275
+
276
+ # mean
277
+ obj[:global_mean] = @global_mean
278
+
279
+ unless (@user_info_map.keys + @item_info_map.keys).all? { |v| v.is_a?(Symbol) }
280
+ raise "Side info keys must be symbols to save"
281
+ end
282
+
283
+ # side info
284
+ obj[:user_info_ids] = @user_info_map.keys
285
+ obj[:item_info_ids] = @item_info_map.keys
286
+ obj[:user_info_factors] = json_dump_ptr(@c)
287
+ obj[:item_info_factors] = json_dump_ptr(@d)
288
+
289
+ # implicit features
290
+ obj[:add_implicit_features] = @add_implicit_features
291
+ obj[:user_factors_implicit] = json_dump_ptr(@ai)
292
+ obj[:item_factors_implicit] = json_dump_ptr(@bi)
293
+
294
+ unless @implicit
295
+ obj[:min_rating] = @min_rating
296
+ obj[:max_rating] = @max_rating
297
+ end
298
+
299
+ obj[:user_means] = json_dump_ptr(@u_colmeans)
300
+
301
+ JSON.generate(obj)
302
+ end
303
+
304
+ def self.load_json(json)
305
+ require "json"
306
+
307
+ obj = JSON.parse(json)
308
+
309
+ recommender = new
310
+ recommender.send(:json_load, obj)
311
+ recommender
312
+ end
313
+
123
314
  private
124
315
 
125
316
  def user_index
@@ -210,7 +401,6 @@ module Cmfrec
210
401
  x_full = nil
211
402
  weight = nil
212
403
  lam_unique = nil
213
- l1_lambda = 0
214
404
  l1_lam_unique = nil
215
405
 
216
406
  uu = nil
@@ -247,7 +437,7 @@ module Cmfrec
247
437
  @m, @n, @k,
248
438
  x_row, x_col, x, nnz,
249
439
  @lambda_, lam_unique,
250
- l1_lambda, l1_lam_unique,
440
+ @l1_lambda, l1_lam_unique,
251
441
  uu, @m_u, p_,
252
442
  ii, @n_i, q,
253
443
  u_row, u_col, u_sp, nnz_u,
@@ -257,12 +447,13 @@ module Cmfrec
257
447
  @w_main, @w_user, @w_item, real_ptr([@w_main_multiplier]),
258
448
  @alpha, @adjust_weight, @apply_log_transf,
259
449
  @niter, @nthreads, @verbose, @handle_interrupt,
260
- @use_cg, @max_cg_steps, @finalize_chol,
450
+ @use_cg, @max_cg_steps, @precondition_cg, @finalize_chol,
261
451
  @nonneg, @max_cd_steps, @nonneg_c, @nonneg_d,
262
452
  @precompute_for_predictions,
263
453
  nil, #precomputedBtB,
264
454
  nil, #precomputedBeTBe,
265
- nil #precomputedBeTBeChol
455
+ nil, #precomputedBeTBeChol
456
+ nil #precomputedCtUbias
266
457
  ]
267
458
  check_status FFI.fit_collective_implicit_als(*fiddle_args(args))
268
459
 
@@ -281,9 +472,9 @@ module Cmfrec
281
472
 
282
473
  glob_mean = Fiddle::Pointer.malloc(Fiddle::SIZEOF_DOUBLE)
283
474
 
284
- center = true
285
- scale_lam = false
286
- scale_lam_sideinfo = false
475
+ # TODO add
476
+ scaling_bias_a = nil
477
+ scaling_bias_b = nil
287
478
 
288
479
  args = [
289
480
  @bias_a, @bias_b,
@@ -298,10 +489,11 @@ module Cmfrec
298
489
  x_row, x_col, x, nnz,
299
490
  x_full,
300
491
  weight,
301
- @user_bias, @item_bias, center,
492
+ @user_bias, @item_bias, @center,
302
493
  @lambda_, lam_unique,
303
- l1_lambda, l1_lam_unique,
304
- scale_lam, scale_lam_sideinfo,
494
+ @l1_lambda, l1_lam_unique,
495
+ @scale_lam, @scale_lam_sideinfo,
496
+ @scale_bias_const, scaling_bias_a, scaling_bias_b,
305
497
  uu, @m_u, p_,
306
498
  ii, @n_i, q,
307
499
  u_row, u_col, u_sp, nnz_u,
@@ -310,7 +502,7 @@ module Cmfrec
310
502
  @k_main, @k_user, @k_item,
311
503
  @w_main, @w_user, @w_item, @w_implicit,
312
504
  @niter, @nthreads, @verbose, @handle_interrupt,
313
- @use_cg, @max_cg_steps, @finalize_chol,
505
+ @use_cg, @max_cg_steps, @precondition_cg, @finalize_chol,
314
506
  @nonneg, @max_cd_steps, @nonneg_c, @nonneg_d,
315
507
  @precompute_for_predictions,
316
508
  @include_all_x,
@@ -321,7 +513,8 @@ module Cmfrec
321
513
  nil, #precomputedBeTBeChol,
322
514
  nil, #precomputedBiTBi,
323
515
  nil, #precomputedTransCtCinvCt,
324
- nil #precomputedCtCw
516
+ nil, #precomputedCtCw
517
+ nil, #precomputedCtUbias
325
518
  ]
326
519
  check_status FFI.fit_collective_explicit_als(*fiddle_args(args))
327
520
 
@@ -336,21 +529,20 @@ module Cmfrec
336
529
  end
337
530
 
338
531
  def set_params(
339
- k: 40, lambda_: 1e+1, method: "als", use_cg: true, user_bias: true,
340
- item_bias: true, add_implicit_features: false,
532
+ k: 40, lambda_: 10.0, method: "als", use_cg: true,
533
+ user_bias: true, item_bias: true, center: true, add_implicit_features: false,
534
+ scale_lam: false, scale_lam_sideinfo: false, scale_bias_const: false,
341
535
  k_user: 0, k_item: 0, k_main: 0,
342
536
  w_main: 1.0, w_user: 1.0, w_item: 1.0, w_implicit: 0.5,
537
+ l1_lambda: 0.0, center_u: true, center_i: true,
343
538
  maxiter: 800, niter: 10, parallelize: "separate", corr_pairs: 4,
344
- max_cg_steps: 3, finalize_chol: true,
539
+ max_cg_steps: 3, precondition_cg: false, finalize_chol: true,
345
540
  na_as_zero: false, na_as_zero_user: false, na_as_zero_item: false,
346
541
  nonneg: false, nonneg_c: false, nonneg_d: false, max_cd_steps: 100,
347
542
  precompute_for_predictions: true, include_all_x: true,
348
- use_float: false,
349
- random_state: 1, verbose: true, print_every: 10,
350
- handle_interrupt: true, produce_dicts: false,
351
- copy_data: true, nthreads: -1
543
+ use_float: true, random_state: 1, verbose: true, print_every: 10,
544
+ handle_interrupt: true, produce_dicts: false, nthreads: -1
352
545
  )
353
-
354
546
  @k = k
355
547
  @k_user = k_user
356
548
  @k_item = k_item
@@ -386,9 +578,17 @@ module Cmfrec
386
578
  @random_state = random_state.to_i
387
579
  @produce_dicts = !!produce_dicts
388
580
  @handle_interrupt = !!handle_interrupt
389
- @copy_data = !!copy_data
390
581
  nthreads = Etc.nprocessors if nthreads < 0
391
582
  @nthreads = nthreads
583
+
584
+ @center = center
585
+ @scale_lam = scale_lam
586
+ @scale_lam_sideinfo = scale_lam_sideinfo
587
+ @scale_bias_const = scale_bias_const
588
+ @l1_lambda = l1_lambda
589
+ @precondition_cg = precondition_cg
590
+
591
+ # TODO center_u, center_i
392
592
  end
393
593
 
394
594
  def update_maps(train_set)
@@ -462,7 +662,7 @@ module Cmfrec
462
662
  end
463
663
  end
464
664
 
465
- def top_n(a_vec:, a_bias:, count:, rated: nil, item_ids: nil)
665
+ def prepare_top_n(count: nil, rated: nil, item_ids: nil)
466
666
  if item_ids
467
667
  # remove missing ids
468
668
  item_ids = item_ids.map { |v| @item_map[v] }.compact
@@ -471,8 +671,7 @@ module Cmfrec
471
671
  include_ix = int_ptr(item_ids)
472
672
  n_include = item_ids.size
473
673
 
474
- # TODO uncomment in 0.2.0
475
- count = n_include # if n_include < count
674
+ count = n_include if n_include < count
476
675
  else
477
676
  include_ix = nil
478
677
  n_include = 0
@@ -494,145 +693,54 @@ module Cmfrec
494
693
  outp_ix = Fiddle::Pointer.malloc(count * Fiddle::SIZEOF_INT)
495
694
  outp_score = Fiddle::Pointer.malloc(count * Fiddle::SIZEOF_DOUBLE)
496
695
 
497
- check_status FFI.topN(
498
- a_vec, @k_user,
499
- @b, @k_item,
500
- @bias_b, @global_mean, a_bias,
501
- @k, @k_main,
502
- include_ix, n_include,
503
- exclude_ix, n_exclude,
504
- outp_ix, outp_score,
505
- count, @n,
506
- @nthreads
507
- )
508
-
509
- imap = @item_map.map(&:reverse).to_h
510
- item_ids = int_array(outp_ix).map { |v| imap[v] }
511
- scores = real_array(outp_score)
512
-
513
- item_ids.zip(scores).map do |item_id, score|
514
- {item_id: item_id, score: score}
515
- end
696
+ [include_ix, n_include, exclude_ix, n_exclude, outp_ix, outp_score, count]
516
697
  end
517
698
 
518
- def factors_warm(data, user_info: nil)
519
- data = to_dataset(data)
520
- user_info = to_dataset(user_info) if user_info
521
-
522
- # remove unknown items
523
- data, unknown_data = data.partition { |d| @item_map[d[:item_id]] }
524
-
525
- if unknown_data.any?
526
- # TODO warn for unknown items?
527
- # warn "[cmfrec] Unknown items: #{unknown_data.map { |d| d[:item_id] }.join(", ")}"
528
- end
529
-
530
- item_ids = data.map { |d| @item_map[d[:item_id]] }
531
-
532
- nnz = data.size
533
- a_vec = Fiddle::Pointer.malloc((@k_user + @k + @k_main) * Fiddle::SIZEOF_DOUBLE)
534
- bias_a = Fiddle::Pointer.malloc(Fiddle::SIZEOF_DOUBLE)
535
-
536
- u_vec_sp = []
537
- u_vec_x_col = []
538
- if user_info
539
- user_info.each do |k, v|
540
- next if k == :user_id
541
-
542
- uc = @user_info_map[k]
543
- raise "Bad key: #{k}" unless uc
544
-
545
- u_vec_x_col << uc
546
- u_vec_sp << v
547
- end
548
- end
549
- p_ = @user_info_map.size
550
- nnz_u_vec = u_vec_sp.size
551
- u_vec_x_col = int_ptr(u_vec_x_col)
552
- u_vec_sp = real_ptr(u_vec_sp)
553
-
554
- u_vec = nil
555
- u_bin_vec = nil
556
- pbin = 0
557
-
558
- weight = nil
559
- lam_unique = nil
560
- l1_lambda = 0
561
- l1_lam_unique = nil
562
- n_max = @n
563
-
564
- if data.any?
565
- if @implicit
566
- ratings = data.map { |d| d[:value] || 1 }
567
- else
568
- ratings = data.map { |d| d[:rating] }
569
- check_ratings(ratings)
570
- end
571
- xa = real_ptr(ratings)
572
- x_col = int_ptr(item_ids)
573
- else
574
- xa = nil
575
- x_col = nil
576
- end
577
- xa_dense = nil
699
+ def top_n(a_vec:, a_bias:, count:, rated: nil, item_ids: nil, row_index:)
700
+ prep = prepare_top_n(count: count, rated: rated, item_ids: item_ids)
701
+ return [] if prep.empty?
702
+ include_ix, n_include, exclude_ix, n_exclude, outp_ix, outp_score, count = prep
578
703
 
579
704
  if @implicit
580
- args = [
705
+ check_status FFI.topN_old_collective_implicit(
581
706
  a_vec,
582
- u_vec, p_,
583
- u_vec_sp, u_vec_x_col, nnz_u_vec,
584
- @na_as_zero_user,
585
- @nonneg,
586
- @u_colmeans,
587
- @b, @n, @c,
588
- xa, x_col, nnz,
707
+ @a, row_index,
708
+ @b,
589
709
  @k, @k_user, @k_item, @k_main,
590
- @lambda_, l1_lambda, @alpha,
591
- @w_main, @w_user, @w_main_multiplier,
592
- @apply_log_transf,
593
- nil, #BeTBe,
594
- nil, #BtB
595
- nil #BeTBeChol
596
- ]
597
- check_status FFI.factors_collective_implicit_single(*fiddle_args(args))
710
+ include_ix, n_include,
711
+ exclude_ix, n_exclude,
712
+ outp_ix, outp_score,
713
+ count, @n, @nthreads
714
+ )
598
715
  else
599
- cb = nil
600
-
601
- scale_lam = false
602
- scale_lam_sideinfo = false
603
-
604
- args = [
605
- a_vec, bias_a,
606
- u_vec, p_,
607
- u_vec_sp, u_vec_x_col, nnz_u_vec,
608
- u_bin_vec, pbin,
609
- @na_as_zero_user, @na_as_zero,
610
- @nonneg,
611
- @c, cb,
612
- @global_mean, @bias_b, @u_colmeans,
613
- xa, x_col, nnz, xa_dense,
614
- @n, weight, @b, @bi,
615
- @add_implicit_features,
716
+ # TODO add param
717
+ n_max = @n
718
+
719
+ check_status FFI.topN_old_collective_explicit(
720
+ a_vec, a_bias,
721
+ @a, @bias_a, row_index,
722
+ @b,
723
+ @bias_b,
724
+ @global_mean,
616
725
  @k, @k_user, @k_item, @k_main,
617
- @lambda_, lam_unique,
618
- l1_lambda, l1_lam_unique,
619
- scale_lam, scale_lam_sideinfo,
620
- @w_main, @w_user, @w_implicit,
621
- n_max,
622
- @include_all_x,
623
- nil, #BtB,
624
- nil, #TransBtBinvBt,
625
- nil, #BtXbias,
626
- nil, #BeTBeChol,
627
- nil, #BiTBi,
628
- nil, #CtCw,
629
- nil, #TransCtCinvCt,
630
- nil #B_plus_bias
631
- ]
632
- check_status FFI.factors_collective_explicit_single(*fiddle_args(args))
726
+ include_ix, n_include,
727
+ exclude_ix, n_exclude,
728
+ outp_ix, outp_score,
729
+ count, @n, n_max, @include_all_x ? 1 : 0, @nthreads
730
+ )
633
731
  end
634
732
 
635
- [a_vec, real_array(bias_a).first, item_ids.uniq]
733
+ top_n_output(outp_ix, outp_score)
734
+ end
735
+
736
+ def top_n_output(outp_ix, outp_score)
737
+ imap = @item_map.map(&:reverse).to_h
738
+ item_ids = int_array(outp_ix).map { |v| imap[v] }
739
+ scores = real_array(outp_score)
740
+
741
+ item_ids.zip(scores).map do |item_id, score|
742
+ {item_id: item_id, score: score}
743
+ end
636
744
  end
637
745
 
638
746
  # convert boolean to int
@@ -810,5 +918,70 @@ module Cmfrec
810
918
 
811
919
  @fit = @m > 0
812
920
  end
921
+
922
+ def json_dump_ptr(ptr)
923
+ Base64.strict_encode64(ptr.to_s(ptr.size)) if ptr
924
+ end
925
+
926
+ def json_load_ptr(str)
927
+ Fiddle::Pointer[Base64.strict_decode64(str)] if str
928
+ end
929
+
930
+ def json_load(obj)
931
+ require "base64"
932
+
933
+ @implicit = obj["implicit"]
934
+
935
+ # options
936
+ set_params(
937
+ k: obj["factors"],
938
+ niter: obj["epochs"],
939
+ verbose: obj["verbose"],
940
+ user_bias: !obj["user_bias"].nil?,
941
+ item_bias: !obj["item_bias"].nil?,
942
+ add_implicit_features: obj["add_implicit_features"]
943
+ )
944
+
945
+ # factors
946
+ @user_map = obj["user_ids"].map.with_index.to_h
947
+ @item_map = obj["item_ids"].map.with_index.to_h
948
+ @rated = obj["rated"].map.with_index.to_h { |r, i| [i, r.to_h { |v| [v, true] }] }
949
+ @a = json_load_ptr(obj["user_factors"])
950
+ @b = json_load_ptr(obj["item_factors"])
951
+
952
+ # bias
953
+ @bias_a = json_load_ptr(obj["user_bias"])
954
+ @bias_b = json_load_ptr(obj["item_bias"])
955
+
956
+ # mean
957
+ @global_mean = obj["global_mean"]
958
+
959
+ # side info
960
+ @user_info_map = obj["user_info_ids"].map(&:to_sym).map.with_index.to_h
961
+ @item_info_map = obj["item_info_ids"].map(&:to_sym).map.with_index.to_h
962
+ @c = json_load_ptr(obj["user_info_factors"])
963
+ @d = json_load_ptr(obj["item_info_factors"])
964
+
965
+ # implicit features
966
+ @add_implicit_features = obj["add_implicit_features"]
967
+ @ai = json_load_ptr(obj["user_factors_implicit"])
968
+ @bi = json_load_ptr(obj["item_factors_implicit"])
969
+
970
+ unless @implicit
971
+ @min_rating = obj["min_rating"]
972
+ @max_rating = obj["max_rating"]
973
+ end
974
+
975
+ @u_colmeans = json_load_ptr(obj["user_means"])
976
+
977
+ @m = @user_map.size
978
+ @n = @item_map.size
979
+ @m_u = @user_info_map.size
980
+ @n_i = @item_info_map.size
981
+
982
+ set_implicit_vars if @implicit
983
+
984
+ @fit = @m > 0
985
+ end
813
986
  end
814
987
  end
@@ -1,3 +1,3 @@
1
1
  module Cmfrec
2
- VERSION = "0.1.6"
2
+ VERSION = "0.2.1"
3
3
  end
data/lib/cmfrec.rb CHANGED
@@ -15,19 +15,19 @@ module Cmfrec
15
15
  class << self
16
16
  attr_accessor :ffi_lib
17
17
  end
18
- lib_name =
18
+ lib_path =
19
19
  if Gem.win_platform?
20
- "cmfrec.dll"
20
+ "x64-mingw/cmfrec.dll"
21
21
  elsif RbConfig::CONFIG["host_os"] =~ /darwin/i
22
- if RbConfig::CONFIG["host_cpu"] =~ /arm/i
23
- "libcmfrec.arm64.dylib"
22
+ if RbConfig::CONFIG["host_cpu"] =~ /arm|aarch64/i
23
+ "arm64-darwin/libcmfrec.dylib"
24
24
  else
25
- "libcmfrec.dylib"
25
+ "x86_64-darwin/libcmfrec.dylib"
26
26
  end
27
27
  else
28
- "libcmfrec.so"
28
+ "x86_64-linux/libcmfrec.so"
29
29
  end
30
- vendor_lib = File.expand_path("../vendor/#{lib_name}", __dir__)
30
+ vendor_lib = File.expand_path("../vendor/#{lib_path}", __dir__)
31
31
  self.ffi_lib = [vendor_lib]
32
32
 
33
33
  # friendlier error message
@@ -0,0 +1,57 @@
1
+ For the included LFBGS library (files "lbfgs.h", "lbfgs.c", "arithmetic_ansi.h"):
2
+
3
+ The MIT License
4
+
5
+ Copyright (c) 1990 Jorge Nocedal
6
+ Copyright (c) 2007-2010 Naoaki Okazaki
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a
9
+ copy of this software and associated documentation files (the "Software"),
10
+ to deal in the Software without restriction, including without limitation
11
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
12
+ and/or sell copies of the Software, and to permit persons to whom the
13
+ Software is furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in
16
+ all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ THE SOFTWARE.
25
+
26
+ For the included ziggurat tables (file "ziggurat.h"):
27
+
28
+ Copyright (c) 2005-2022, NumPy Developers.
29
+ All rights reserved.
30
+
31
+ Redistribution and use in source and binary forms, with or without
32
+ modification, are permitted provided that the following conditions are
33
+ met:
34
+
35
+ * Redistributions of source code must retain the above copyright
36
+ notice, this list of conditions and the following disclaimer.
37
+
38
+ * Redistributions in binary form must reproduce the above
39
+ copyright notice, this list of conditions and the following
40
+ disclaimer in the documentation and/or other materials provided
41
+ with the distribution.
42
+
43
+ * Neither the name of the NumPy Developers nor the names of any
44
+ contributors may be used to endorse or promote products derived
45
+ from this software without specific prior written permission.
46
+
47
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
50
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
51
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
53
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
54
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
55
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
56
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
57
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020-2022 David Cortes
4
+
5
+ All rights reserved.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to
9
+ deal in the Software without restriction, including without limitation the
10
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11
+ sell copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23
+ IN THE SOFTWARE.
Binary file
@@ -0,0 +1,57 @@
1
+ For the included LFBGS library (files "lbfgs.h", "lbfgs.c", "arithmetic_ansi.h"):
2
+
3
+ The MIT License
4
+
5
+ Copyright (c) 1990 Jorge Nocedal
6
+ Copyright (c) 2007-2010 Naoaki Okazaki
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a
9
+ copy of this software and associated documentation files (the "Software"),
10
+ to deal in the Software without restriction, including without limitation
11
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
12
+ and/or sell copies of the Software, and to permit persons to whom the
13
+ Software is furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in
16
+ all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ THE SOFTWARE.
25
+
26
+ For the included ziggurat tables (file "ziggurat.h"):
27
+
28
+ Copyright (c) 2005-2022, NumPy Developers.
29
+ All rights reserved.
30
+
31
+ Redistribution and use in source and binary forms, with or without
32
+ modification, are permitted provided that the following conditions are
33
+ met:
34
+
35
+ * Redistributions of source code must retain the above copyright
36
+ notice, this list of conditions and the following disclaimer.
37
+
38
+ * Redistributions in binary form must reproduce the above
39
+ copyright notice, this list of conditions and the following
40
+ disclaimer in the documentation and/or other materials provided
41
+ with the distribution.
42
+
43
+ * Neither the name of the NumPy Developers nor the names of any
44
+ contributors may be used to endorse or promote products derived
45
+ from this software without specific prior written permission.
46
+
47
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
50
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
51
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
53
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
54
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
55
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
56
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
57
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020-2022 David Cortes
4
+
5
+ All rights reserved.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to
9
+ deal in the Software without restriction, including without limitation the
10
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11
+ sell copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23
+ IN THE SOFTWARE.
@@ -0,0 +1,57 @@
1
+ For the included LFBGS library (files "lbfgs.h", "lbfgs.c", "arithmetic_ansi.h"):
2
+
3
+ The MIT License
4
+
5
+ Copyright (c) 1990 Jorge Nocedal
6
+ Copyright (c) 2007-2010 Naoaki Okazaki
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a
9
+ copy of this software and associated documentation files (the "Software"),
10
+ to deal in the Software without restriction, including without limitation
11
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
12
+ and/or sell copies of the Software, and to permit persons to whom the
13
+ Software is furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in
16
+ all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ THE SOFTWARE.
25
+
26
+ For the included ziggurat tables (file "ziggurat.h"):
27
+
28
+ Copyright (c) 2005-2022, NumPy Developers.
29
+ All rights reserved.
30
+
31
+ Redistribution and use in source and binary forms, with or without
32
+ modification, are permitted provided that the following conditions are
33
+ met:
34
+
35
+ * Redistributions of source code must retain the above copyright
36
+ notice, this list of conditions and the following disclaimer.
37
+
38
+ * Redistributions in binary form must reproduce the above
39
+ copyright notice, this list of conditions and the following
40
+ disclaimer in the documentation and/or other materials provided
41
+ with the distribution.
42
+
43
+ * Neither the name of the NumPy Developers nor the names of any
44
+ contributors may be used to endorse or promote products derived
45
+ from this software without specific prior written permission.
46
+
47
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
50
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
51
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
53
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
54
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
55
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
56
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
57
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020-2022 David Cortes
4
+
5
+ All rights reserved.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to
9
+ deal in the Software without restriction, including without limitation the
10
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11
+ sell copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23
+ IN THE SOFTWARE.
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cmfrec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.1
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-08-12 00:00:00.000000000 Z
11
+ date: 2022-07-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: andrew@ankane.org
@@ -24,11 +24,16 @@ files:
24
24
  - lib/cmfrec/ffi.rb
25
25
  - lib/cmfrec/recommender.rb
26
26
  - lib/cmfrec/version.rb
27
- - vendor/LICENSE.txt
28
- - vendor/libcmfrec.arm64.dylib
29
- - vendor/libcmfrec.dylib
30
- - vendor/libcmfrec.so
31
- homepage: https://github.com/ankane/cmfrec
27
+ - vendor/arm64-darwin/COPYRIGHTS
28
+ - vendor/arm64-darwin/LICENSE
29
+ - vendor/arm64-darwin/libcmfrec.dylib
30
+ - vendor/x86_64-darwin/COPYRIGHTS
31
+ - vendor/x86_64-darwin/LICENSE
32
+ - vendor/x86_64-darwin/libcmfrec.dylib
33
+ - vendor/x86_64-linux/COPYRIGHTS
34
+ - vendor/x86_64-linux/LICENSE
35
+ - vendor/x86_64-linux/libcmfrec.so
36
+ homepage: https://github.com/ankane/cmfrec-ruby
32
37
  licenses:
33
38
  - MIT
34
39
  metadata: {}
@@ -40,14 +45,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
40
45
  requirements:
41
46
  - - ">="
42
47
  - !ruby/object:Gem::Version
43
- version: '2.5'
48
+ version: '2.7'
44
49
  required_rubygems_version: !ruby/object:Gem::Requirement
45
50
  requirements:
46
51
  - - ">="
47
52
  - !ruby/object:Gem::Version
48
53
  version: '0'
49
54
  requirements: []
50
- rubygems_version: 3.2.22
55
+ rubygems_version: 3.3.7
51
56
  signing_key:
52
57
  specification_version: 4
53
58
  summary: Recommendations for Ruby using collective matrix factorization
data/vendor/LICENSE.txt DELETED
@@ -1,74 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2020 David Cortes
4
-
5
- All rights reserved.
6
-
7
- Permission is hereby granted, free of charge, to any person obtaining a copy
8
- of this software and associated documentation files (the "Software"), to
9
- deal in the Software without restriction, including without limitation the
10
- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11
- sell copies of the Software, and to permit persons to whom the Software is
12
- furnished to do so, subject to the following conditions:
13
-
14
- The above copyright notice and this permission notice shall be included in
15
- all copies or substantial portions of the Software.
16
-
17
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23
- IN THE SOFTWARE.
24
-
25
- ---
26
-
27
- ANSI C implementation of vector operations.
28
-
29
- Copyright (c) 2007-2010 Naoaki Okazaki
30
- All rights reserved.
31
-
32
- Permission is hereby granted, free of charge, to any person obtaining a copy
33
- of this software and associated documentation files (the "Software"), to deal
34
- in the Software without restriction, including without limitation the rights
35
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36
- copies of the Software, and to permit persons to whom the Software is
37
- furnished to do so, subject to the following conditions:
38
-
39
- The above copyright notice and this permission notice shall be included in
40
- all copies or substantial portions of the Software.
41
-
42
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
48
- THE SOFTWARE.
49
-
50
- ---
51
-
52
- C library of Limited memory BFGS (L-BFGS).
53
-
54
- Copyright (c) 1990, Jorge Nocedal
55
- Copyright (c) 2007-2010 Naoaki Okazaki
56
- All rights reserved.
57
-
58
- Permission is hereby granted, free of charge, to any person obtaining a copy
59
- of this software and associated documentation files (the "Software"), to deal
60
- in the Software without restriction, including without limitation the rights
61
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
62
- copies of the Software, and to permit persons to whom the Software is
63
- furnished to do so, subject to the following conditions:
64
-
65
- The above copyright notice and this permission notice shall be included in
66
- all copies or substantial portions of the Software.
67
-
68
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
69
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
70
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
71
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
72
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
73
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
74
- THE SOFTWARE.
Binary file
Binary file
data/vendor/libcmfrec.so DELETED
Binary file