lingua_rs 0.4.5 → 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: 953c59791dc0b1234e1264f3e75881319edfd752a537a3bb0fd02c9830cb63ea
4
- data.tar.gz: 6cf3ce511f0026deb1090c531771a843bb15e0fc4c7637e974f1d50b65330106
3
+ metadata.gz: 9ec0e0ba5844b9b3e47cd60babbc90ac03791bc83e3327e78b7f2d83ba4a71d8
4
+ data.tar.gz: 55af5915e63a1b34cbc5c73d2e2c63b6a1ce2030183112c54aad888b299bc889
5
5
  SHA512:
6
- metadata.gz: 067a63ee83c014a358fa48f0df452c626123d8039862cb9dac57b4e733fb379fc6af97102613bd8c664b3762105123223a488b962896bfc419195d237ff6135f
7
- data.tar.gz: 4336e0b72d1dff76ddf28d2721025b4a36a4f6dd031252a09bdfa1eb10fdfc6982dcbc1bbd01d223a84e0f5256c06a98190c4c47a0f154ba06696e4a60abfce7
6
+ metadata.gz: b6bfc5d1605bf171e7be4dd899e7b82460f4c8b81ab541ff97a18fb59205a752e9cfe12de640c20b6a60a4814bc8e89e884b9d501a2ec3b5a4e4f0b4bc2e094d
7
+ data.tar.gz: ecf2866f980b9ddd7189d639ea1330a4f47f8f0a528dcb636358c7c6de30add2d8a854cee8252eac36540fc528b1836eedebbd03313eeb1de167cd18c241afbf
data/README.md CHANGED
@@ -66,8 +66,6 @@ Lingua.detect('Bonjour', languages: %w[en french deu])
66
66
 
67
67
  ### Options
68
68
 
69
- All options accept both symbol and string keys.
70
-
71
69
  | Option | Type | Description |
72
70
  |---|---|---|
73
71
  | `languages` | `Array<String>` | Restrict detection to these languages (names, ISO 639-1 or ISO 639-3 codes) |
@@ -132,7 +130,52 @@ detector = Lingua::Detector.new(languages: %w[en fr de])
132
130
  detector.detect_multiple(text)
133
131
  ```
134
132
 
135
- ### `Lingua::Language` methods
133
+ ### Parallel batch detection
134
+
135
+ `Lingua::Detector` provides batch methods that process multiple texts in parallel using [Rayon](https://github.com/rayon-rs/rayon). These are significantly faster than calling single-text methods in a loop.
136
+
137
+ ```ruby
138
+ detector = Lingua::Detector.new(languages: %w[en fr de])
139
+
140
+ # Detect language of multiple texts
141
+ detector.detect_batch(['Bonjour le monde', 'Hello world', 'Hallo Welt'])
142
+ # => [#<Lingua::Language French>, #<Lingua::Language English>, #<Lingua::Language German>]
143
+
144
+ # Confidence for a specific language across multiple texts
145
+ detector.confidence_batch(['Bonjour le monde', 'Hello world'], :fr)
146
+ # => [0.8217, 0.0215]
147
+
148
+ # Confidence values for multiple texts
149
+ detector.confidence_values_batch(['Bonjour le monde', 'Hello world'])
150
+ # => [[#<Lingua::ConfidenceResult French (0.82)>, ...], [#<Lingua::ConfidenceResult English (0.91)>, ...]]
151
+
152
+ # Mixed-language detection on multiple texts
153
+ detector.detect_multiple_batch(['Bonjour le monde. Hello world.', 'Ich bin müde. Salut tout le monde.'])
154
+ # => [[#<Lingua::Segment French ...>, #<Lingua::Segment English ...>], [#<Lingua::Segment German ...>, #<Lingua::Segment French ...>]]
155
+ ```
156
+
157
+ | Method | Return type | Description |
158
+ |---|---|---|
159
+ | `detect_batch(texts)` | `Array<Language\|nil>` | Detect language of each text |
160
+ | `detect_multiple_batch(texts)` | `Array<Array<Segment>>` | Mixed-language detection for each text |
161
+ | `confidence_values_batch(texts)` | `Array<Array<ConfidenceResult>>` | Confidence values for each text |
162
+ | `confidence_batch(texts, language)` | `Array<Float>` | Confidence for a specific language across texts |
163
+
164
+ ### Error handling
165
+
166
+ `Lingua::UnknownLanguageError` (subclass of `ArgumentError`) is raised when an unrecognized language name or code is passed:
167
+
168
+ ```ruby
169
+ begin
170
+ Lingua.detect('Hello', languages: %w[en zzzz])
171
+ rescue Lingua::UnknownLanguageError => e
172
+ puts e.message # => unknown language: "zzzz"
173
+ end
174
+ ```
175
+
176
+ ## API Reference
177
+
178
+ ### `Lingua::Language`
136
179
 
137
180
  `Lingua::Language` objects support equality (`==`) and can be used as Hash keys. You can look up a language by name, ISO 639-1 code, or ISO 639-3 code using `[]`:
138
181
 
@@ -163,7 +206,7 @@ Lingua::Language['xxx'] # => nil
163
206
  | `==` | `Boolean` | Compare two languages |
164
207
  | `hash` | `Integer` | Hash value (usable as Hash key) |
165
208
 
166
- ### `Lingua::ConfidenceResult` methods
209
+ ### `Lingua::ConfidenceResult`
167
210
 
168
211
  Returned by `confidence_values`.
169
212
 
@@ -174,7 +217,7 @@ Returned by `confidence_values`.
174
217
  | `to_s` | `String` | `'French (0.82)'` |
175
218
  | `inspect` | `String` | `'#<Lingua::ConfidenceResult French (0.8217)>'` |
176
219
 
177
- ### `Lingua::Segment` methods
220
+ ### `Lingua::Segment`
178
221
 
179
222
  Returned by `detect_multiple`.
180
223
 
@@ -188,18 +231,6 @@ Returned by `detect_multiple`.
188
231
  | `to_s` | `String` | `'French (0-22): Parlez-vous français? '` |
189
232
  | `inspect` | `String` | `'#<Lingua::Segment French (0-22) "Parlez-vous français? ">'` |
190
233
 
191
- ### Error handling
192
-
193
- `Lingua::UnknownLanguageError` (subclass of `ArgumentError`) is raised when an unrecognized language name or code is passed:
194
-
195
- ```ruby
196
- begin
197
- Lingua.detect('Hello', languages: %w[en zzzz])
198
- rescue Lingua::UnknownLanguageError => e
199
- puts e.message # => unknown language: "zzzz"
200
- end
201
- ```
202
-
203
234
  ## Optimization: selecting languages
204
235
 
205
236
  By default, all 75 languages are compiled into the native extension (~278 MB). If you only need a subset, select languages at build time to reduce binary size and improve detection speed.
@@ -266,6 +297,8 @@ Supported languages (from [Lingua Rust's official list](https://github.com/pemis
266
297
 
267
298
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake compile` to build the native extension and `rake test` to run the tests.
268
299
 
300
+ To run benchmarks (sequential vs batch performance comparison): `rake bench`
301
+
269
302
  ## Acknowledgements
270
303
 
271
304
  This gem is built on top of [Lingua](https://github.com/pemistahl/lingua-rs) by [Peter M. Stahl](https://github.com/pemistahl), a highly accurate natural language detection library written in Rust.
data/Rakefile CHANGED
@@ -19,4 +19,10 @@ RbSys::ExtensionTask.new('lingua', GEMSPEC) do |ext|
19
19
  ext.lib_dir = 'lib/lingua'
20
20
  end
21
21
 
22
+ desc 'Run benchmarks (sequential vs batch)'
23
+ task bench: :compile do
24
+ ENV['BENCH'] = '1'
25
+ ruby '-Itest', 'test/test_benchmark.rb'
26
+ end
27
+
22
28
  task default: %i[compile test rubocop]
@@ -13,6 +13,10 @@ pub fn define(ruby: &Ruby, module: &RModule) -> Result<(), Error> {
13
13
  class.define_method("confidence", method!(RubyDetector::confidence, 2))?;
14
14
  class.define_method("confidence_values", method!(RubyDetector::confidence_values, 1))?;
15
15
  class.define_method("detect_multiple", method!(RubyDetector::detect_multiple, 1))?;
16
+ class.define_method("detect_batch", method!(RubyDetector::detect_batch, 1))?;
17
+ class.define_method("detect_multiple_batch", method!(RubyDetector::detect_multiple_batch, 1))?;
18
+ class.define_method("confidence_values_batch", method!(RubyDetector::confidence_values_batch, 1))?;
19
+ class.define_method("confidence_batch", method!(RubyDetector::confidence_batch, 2))?;
16
20
  Ok(())
17
21
  }
18
22
 
@@ -81,6 +85,73 @@ impl RubyDetector {
81
85
  pub fn detect_multiple(&self, subject: String) -> Result<RArray, Error> {
82
86
  compute_detect_multiple(&self.detector, &subject)
83
87
  }
88
+
89
+ pub fn detect_batch(&self, texts: Vec<String>) -> Result<RArray, Error> {
90
+ let ruby = Ruby::get().unwrap();
91
+ let results = self.detector.detect_languages_in_parallel_of(&texts);
92
+ let array = ruby.ary_new_capa(results.len());
93
+ for result in results {
94
+ array.push(result.map(WrappedLanguage))?;
95
+ }
96
+ Ok(array)
97
+ }
98
+
99
+ pub fn detect_multiple_batch(&self, texts: Vec<String>) -> Result<RArray, Error> {
100
+ let ruby = Ruby::get().unwrap();
101
+ let results = self.detector.detect_multiple_languages_in_parallel_of(&texts);
102
+ let outer = ruby.ary_new_capa(results.len());
103
+ for (detection_results, subject) in results.iter().zip(texts.iter()) {
104
+ let inner = ruby.ary_new_capa(detection_results.len());
105
+ for r in detection_results {
106
+ let text = subject[r.start_index()..r.end_index()].to_string();
107
+ let start_index = subject[..r.start_index()].chars().count();
108
+ let end_index = start_index + text.chars().count();
109
+ inner.push(Segment {
110
+ language: r.language(),
111
+ start_index,
112
+ end_index,
113
+ word_count: r.word_count(),
114
+ text,
115
+ })?;
116
+ }
117
+ outer.push(inner)?;
118
+ }
119
+ Ok(outer)
120
+ }
121
+
122
+ pub fn confidence_values_batch(&self, texts: Vec<String>) -> Result<RArray, Error> {
123
+ let ruby = Ruby::get().unwrap();
124
+ let results = self.detector.compute_language_confidence_values_in_parallel(&texts);
125
+ let outer = ruby.ary_new_capa(results.len());
126
+ for values in results {
127
+ let inner = ruby.ary_new_capa(values.len());
128
+ for (language, confidence) in values {
129
+ inner.push(ConfidenceResult {
130
+ language,
131
+ confidence,
132
+ })?;
133
+ }
134
+ outer.push(inner)?;
135
+ }
136
+ Ok(outer)
137
+ }
138
+
139
+ pub fn confidence_batch(&self, texts: Vec<String>, language: magnus::Value) -> Result<RArray, Error> {
140
+ let ruby = Ruby::get().unwrap();
141
+ let language_str = value_to_string(language)?;
142
+ let lang = parse_language(&language_str).ok_or_else(|| {
143
+ Error::new(
144
+ unknown_language_error(&ruby),
145
+ format!("unknown language: \"{language_str}\""),
146
+ )
147
+ })?;
148
+ let results = self.detector.compute_language_confidence_in_parallel(&texts, lang);
149
+ let array = ruby.ary_new_capa(results.len());
150
+ for score in results {
151
+ array.push(score)?;
152
+ }
153
+ Ok(array)
154
+ }
84
155
  }
85
156
 
86
157
  pub fn compute_detect_multiple(
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lingua
4
- VERSION = '0.4.5'
4
+ VERSION = '0.5.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lingua_rs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sébastien Vrillaud