libffm 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2392488b8b459a0eb791ef7adc80fd31451755214eb67b089cb374e517da031f
4
- data.tar.gz: d42c241978ba042497e722a415b12a0ff2011debaa6b72a3f32137c2c89b9b89
3
+ metadata.gz: 07323e72497473a0d74016a0f038bf20e3630975d209ff7f5ba35735d4950c70
4
+ data.tar.gz: 28723358a87d007aff3ee1c5e8cd007272c08fbac568a92c9f02855c41e764c1
5
5
  SHA512:
6
- metadata.gz: e202868004f3b44a112d9c53ff8776767450d5dced1c04e537bd81fb880102df2cd3ecf69a23610be73582b244db2164d86476b96027724140ff029b3256c377
7
- data.tar.gz: ef2e21aa60f27d44131a4041144779509bad24f866dbc5634e46ba1f13b55630af148d4d3b5ff854d116918d001ff6c546f789eeb92398ebae09dd7742277b32
6
+ metadata.gz: f14d21212c9466cd073f29648e880a085910437b404baaa20534f90b1ff9c6e196978eb55172cae353a5fbe35d08fa2aaf8c63cf1c8e613dbf24f9551efc2375
7
+ data.tar.gz: dece73854d082f55ba1aa8b772a44ede96e62f87af3295d01bf4a8de0039e61021e6eb6bc9b8abc64fae10fe74ed3fd6c7ced28e08513ad6ec027381950e1e66
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.5.0 (2026-04-04)
2
+
3
+ - Improved installation time
4
+ - Dropped support for Ruby < 3.3
5
+
6
+ ## 0.4.1 (2025-10-26)
7
+
8
+ - Fixed error with Rice 4.7
9
+
1
10
  ## 0.4.0 (2025-04-03)
2
11
 
3
12
  - Dropped support for Ruby < 3.2
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  Copyright (c) 2017 The LIBFFM Project.
3
- Copyright (c) 2020-2025 Andrew Kane.
3
+ Copyright (c) 2020-2026 Andrew Kane.
4
4
  All rights reserved.
5
5
 
6
6
  Redistribution and use in source and binary forms, with or without
data/ext/libffm/ext.cpp CHANGED
@@ -1,29 +1,21 @@
1
- // stdlib
1
+ #include <cstdio>
2
+ #include <cstdlib>
3
+ #include <cstring>
4
+ #include <fstream>
2
5
  #include <iostream>
6
+ #include <string>
7
+ #include <string_view>
8
+ #include <vector>
3
9
 
4
- // ffm
5
10
  #include <ffm.h>
6
-
7
- // rice
8
11
  #include <rice/rice.hpp>
9
- #include <rice/stl.hpp>
10
-
11
- using Rice::Array;
12
- using Rice::Class;
13
- using Rice::Module;
14
- using Rice::Object;
15
- using Rice::String;
16
- using Rice::define_module;
17
- using Rice::define_module_under;
18
- using Rice::define_class_under;
19
12
 
20
13
  extern "C"
21
- void Init_ext()
22
- {
23
- auto rb_mLibffm = define_module("Libffm");
14
+ void Init_ext() {
15
+ auto rb_mLibffm = Rice::define_module("Libffm");
24
16
 
25
- auto rb_mExt = define_module_under(rb_mLibffm, "Ext");
26
- define_class_under<ffm::ffm_model>(rb_mExt, "Model");
17
+ auto rb_mExt = Rice::define_module_under(rb_mLibffm, "Ext");
18
+ Rice::define_class_under<ffm::ffm_model>(rb_mExt, "Model");
27
19
 
28
20
  rb_mExt
29
21
  .define_singleton_function(
@@ -33,17 +25,17 @@ void Init_ext()
33
25
  })
34
26
  .define_singleton_function(
35
27
  "fit",
36
- [](const std::string& tr_path, const std::string& va_path, const std::string& tmp_prefix, ffm::ffm_float eta, ffm::ffm_float lambda, ffm::ffm_int nr_iters, ffm::ffm_int k, bool normalization, bool auto_stop) {
28
+ [](Rice::String tr_path, Rice::String va_path, Rice::String tmp_prefix, ffm::ffm_float eta, ffm::ffm_float lambda, ffm::ffm_int nr_iters, ffm::ffm_int k, bool normalization, bool auto_stop) {
37
29
  // quiet
38
- ffm::cout.setstate(ffm::ios_base::badbit);
30
+ std::cout.setstate(std::ios_base::badbit);
39
31
 
40
- std::string tr_bin_path = tmp_prefix + "train.bin";
41
- ffm::ffm_read_problem_to_disk(tr_path, tr_bin_path);
32
+ std::string tr_bin_path = tmp_prefix.str() + "train.bin";
33
+ ffm::ffm_read_problem_to_disk(tr_path.str(), tr_bin_path);
42
34
 
43
- std::string va_bin_path = "";
44
- if (va_path.size() > 0) {
45
- va_bin_path = tmp_prefix + "validation.bin";
46
- ffm::ffm_read_problem_to_disk(va_path, va_bin_path);
35
+ std::string va_bin_path;
36
+ if (va_path.length() != 0) {
37
+ va_bin_path = tmp_prefix.str() + "validation.bin";
38
+ ffm::ffm_read_problem_to_disk(va_path.str(), va_bin_path);
47
39
  }
48
40
 
49
41
  ffm::ffm_parameter param;
@@ -58,51 +50,75 @@ void Init_ext()
58
50
  })
59
51
  .define_singleton_function(
60
52
  "predict",
61
- [](ffm::ffm_model& model, const std::string& test_path) {
62
- int const kMaxLineSize = 1000000;
53
+ [](ffm::ffm_model& model, Rice::String test_path) {
54
+ std::ifstream f_in(test_path.str());
55
+ if (!f_in.is_open()) {
56
+ throw std::runtime_error{"Cannot open file"};
57
+ }
58
+ std::string line;
63
59
 
64
- FILE *f_in = fopen(test_path.c_str(), "r");
65
- char line[kMaxLineSize];
60
+ Rice::Array ret;
61
+ while (std::getline(f_in, line)) {
62
+ std::vector<ffm::ffm_node> x;
66
63
 
67
- ffm::vector<ffm::ffm_node> x;
68
- ffm::ffm_int i = 0;
64
+ std::string_view line_view{line};
69
65
 
70
- Array ret;
71
- for(; fgets(line, kMaxLineSize, f_in) != nullptr; i++) {
72
- x.clear();
73
- strtok(line, " \t");
66
+ size_t n = line_view.find_first_of(" \t");
67
+ if (n == std::string_view::npos) {
68
+ throw std::runtime_error{"Invalid line"};
69
+ }
70
+
71
+ size_t start = n + 1;
74
72
 
75
73
  while (true) {
76
- char *field_char = strtok(nullptr,":");
77
- char *idx_char = strtok(nullptr,":");
78
- char *value_char = strtok(nullptr," \t");
79
- if (field_char == nullptr || *field_char == '\n')
80
- break;
74
+ n = line_view.find_first_of(" \t", start);
75
+
76
+ std::string_view s = n == std::string_view::npos ? line_view.substr(start) : line_view.substr(start, n - start);
81
77
 
82
- ffm::ffm_node N;
83
- N.f = atoi(field_char);
84
- N.j = atoi(idx_char);
85
- N.v = atof(value_char);
78
+ size_t n2 = s.find(':');
79
+ if (n2 == std::string_view::npos) {
80
+ throw std::runtime_error{"Invalid line"};
81
+ }
82
+
83
+ size_t n3 = s.find(':', n2 + 1);
84
+ if (n3 == std::string_view::npos) {
85
+ throw std::runtime_error{"Invalid line"};
86
+ }
87
+
88
+ std::string_view field_char = s.substr(0, n2);
89
+ std::string_view idx_char = s.substr(n2 + 1, n3 - n2 - 1);
90
+ std::string_view value_char = s.substr(n3 + 1);
91
+
92
+ // cannot pass string view to stoi/stof
93
+ ffm::ffm_node N{
94
+ std::stoi(std::string{field_char}),
95
+ std::stoi(std::string{idx_char}),
96
+ std::stof(std::string{value_char})
97
+ };
86
98
 
87
99
  x.push_back(N);
100
+
101
+ if (n == std::string::npos) {
102
+ break;
103
+ }
104
+
105
+ start = n + 1;
88
106
  }
89
107
 
90
- ffm::ffm_float y_bar = ffm::ffm_predict(x.data(), x.data()+x.size(), model);
91
- ret.push(y_bar);
108
+ ffm::ffm_float y_bar = ffm::ffm_predict(x.data(), x.data() + x.size(), model);
109
+ ret.push(y_bar, false);
92
110
  }
93
111
 
94
- fclose(f_in);
95
-
96
112
  return ret;
97
113
  })
98
114
  .define_singleton_function(
99
115
  "save_model",
100
- [](ffm::ffm_model& model, const std::string& path) {
101
- ffm::ffm_save_model(model, path);
116
+ [](ffm::ffm_model& model, Rice::String path) {
117
+ ffm::ffm_save_model(model, path.str());
102
118
  })
103
119
  .define_singleton_function(
104
120
  "load_model",
105
- [](const std::string& path) {
106
- return ffm::ffm_load_model(path);
121
+ [](Rice::String path) {
122
+ return ffm::ffm_load_model(path.str());
107
123
  });
108
124
  }
@@ -1,3 +1,3 @@
1
1
  module Libffm
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/libffm.rb CHANGED
@@ -5,8 +5,8 @@ require "libffm/ext"
5
5
  require "tmpdir"
6
6
 
7
7
  # modules
8
- require "libffm/model"
9
- require "libffm/version"
8
+ require_relative "libffm/model"
9
+ require_relative "libffm/version"
10
10
 
11
11
  module Libffm
12
12
  class Error < StandardError; end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libffm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-03 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rice
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 4.3.3
18
+ version: '4.7'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 4.3.3
25
+ version: '4.7'
26
26
  email: andrew@ankane.org
27
27
  executables: []
28
28
  extensions:
@@ -58,14 +58,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '3.2'
61
+ version: '3.3'
62
62
  required_rubygems_version: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
66
  version: '0'
67
67
  requirements: []
68
- rubygems_version: 3.6.2
68
+ rubygems_version: 4.0.6
69
69
  specification_version: 4
70
70
  summary: Field-aware factorization machines for Ruby
71
71
  test_files: []