1#![allow(unknown_lints)]
2#![allow(clippy::useless_transmute)]
3#![allow(clippy::transmute_ptr_to_ref)]
4#![allow(clippy::transmute_ptr_to_ptr)]
5use std::ffi::{CStr, CString};
6use std::fs::File;
7use std::io::{Cursor, Read, Seek, SeekFrom};
8#[cfg(unix)]
9use std::os::unix::io::{IntoRawFd, RawFd};
10#[cfg(windows)]
11use std::os::windows::io::{IntoRawHandle, RawHandle};
12use std::path::Path;
13use std::{error, fmt, mem, ptr, slice};
14
15use crfsuite_sys::*;
16#[cfg(not(windows))]
17use libc::{c_char, c_int, c_uint};
18use libc::{c_void, fclose, fdopen};
19
20#[derive(Debug, Clone, PartialEq)]
22pub enum CrfSuiteError {
23 Incompatible,
25 InternalLogic,
27 NotImplemented,
29 NotSupported,
31 OutOfMemory,
33 Overflow,
35 Unknown,
37}
38
39impl error::Error for CrfSuiteError {}
40
41impl fmt::Display for CrfSuiteError {
42 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43 let desc = match *self {
44 CrfSuiteError::Incompatible => "Incompatible data",
45 CrfSuiteError::InternalLogic => "Internal error",
46 CrfSuiteError::NotImplemented => "Not implemented",
47 CrfSuiteError::NotSupported => "Unsupported operation",
48 CrfSuiteError::OutOfMemory => "Insufficient memory",
49 CrfSuiteError::Overflow => "Overflow",
50 CrfSuiteError::Unknown => "Unknown error occurred",
51 };
52 write!(f, "{}", desc)
53 }
54}
55
56impl From<libc::c_int> for CrfSuiteError {
57 fn from(code: libc::c_int) -> Self {
58 match code {
59 CRFSUITEERR_INCOMPATIBLE => CrfSuiteError::Incompatible,
60 CRFSUITEERR_INTERNAL_LOGIC => CrfSuiteError::InternalLogic,
61 CRFSUITEERR_NOTIMPLEMENTED => CrfSuiteError::NotImplemented,
62 CRFSUITEERR_NOTSUPPORTED => CrfSuiteError::NotSupported,
63 CRFSUITEERR_OUTOFMEMORY => CrfSuiteError::OutOfMemory,
64 CRFSUITEERR_OVERFLOW => CrfSuiteError::Overflow,
65 CRFSUITEERR_UNKNOWN => CrfSuiteError::Unknown,
66 _ => unreachable!(),
67 }
68 }
69}
70
71#[derive(Debug, Clone, PartialEq)]
72pub enum CrfError {
73 CrfSuiteError(CrfSuiteError),
75 CreateInstanceError(String),
77 ParamNotFound(String),
79 AlgorithmNotSelected,
81 EmptyData,
83 InvalidArgument(String),
85 ValueError(String),
87 InvalidModel(String),
89}
90
91impl error::Error for CrfError {}
92
93impl fmt::Display for CrfError {
94 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95 match *self {
96 CrfError::CrfSuiteError(ref err) => err.fmt(f),
97 CrfError::ParamNotFound(ref name) => write!(f, "Parameter {} not found", name),
98 CrfError::AlgorithmNotSelected => write!(
99 f,
100 "The trainer is not initialized. Call Trainer::select before Trainer::train."
101 ),
102 CrfError::EmptyData => write!(
103 f,
104 "The data is empty. Call Trainer::append before Trainer::train."
105 ),
106 CrfError::CreateInstanceError(ref err)
107 | CrfError::InvalidArgument(ref err)
108 | CrfError::ValueError(ref err)
109 | CrfError::InvalidModel(ref err) => err.fmt(f),
110 }
111 }
112}
113
114pub type Result<T> = ::std::result::Result<T, CrfError>;
115
116#[derive(Debug, Clone, PartialEq)]
118pub struct Attribute {
119 pub name: String,
121 pub value: f64,
123}
124
125pub type Item = Vec<Attribute>;
127
128impl Attribute {
129 #[inline]
130 pub fn new<T: Into<String>>(name: T, value: f64) -> Self {
131 Self {
132 name: name.into(),
133 value,
134 }
135 }
136}
137
138impl From<String> for Attribute {
139 #[inline]
140 fn from(t: String) -> Self {
141 Self {
142 name: t,
143 value: 1.0,
144 }
145 }
146}
147
148impl<'a> From<&'a str> for Attribute {
149 #[inline]
150 fn from(t: &'a str) -> Self {
151 Self {
152 name: t.to_string(),
153 value: 1.0,
154 }
155 }
156}
157
158impl<T: Into<String>> From<(T, f64)> for Attribute {
159 #[inline]
160 fn from(t: (T, f64)) -> Self {
161 let (name, value) = t;
162 Self {
163 name: name.into(),
164 value,
165 }
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq)]
171pub enum Algorithm {
172 LBFGS,
174 L2SGD,
176 AP,
178 PA,
180 AROW,
182}
183
184impl fmt::Display for Algorithm {
185 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186 let desc = match *self {
187 Algorithm::LBFGS => "lbfgs",
188 Algorithm::L2SGD => "l2sgd",
189 Algorithm::AP => "averaged-perceptron",
190 Algorithm::PA => "passive-aggressive",
191 Algorithm::AROW => "arow",
192 };
193 write!(f, "{}", desc)
194 }
195}
196
197impl ::std::str::FromStr for Algorithm {
198 type Err = CrfError;
199
200 fn from_str(s: &str) -> Result<Self> {
201 match s {
202 "lbfgs" => Ok(Algorithm::LBFGS),
203 "l2sgd" => Ok(Algorithm::L2SGD),
204 "ap" | "averaged-perceptron" => Ok(Algorithm::AP),
205 "pa" | "passive-aggressive" => Ok(Algorithm::PA),
206 "arow" => Ok(Algorithm::AROW),
207 _ => Err(CrfError::InvalidArgument(s.to_string())),
208 }
209 }
210}
211
212#[derive(Debug, Clone, Copy, PartialEq)]
214pub enum GraphicalModel {
215 CRF1D,
219}
220
221impl fmt::Display for GraphicalModel {
222 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
223 let desc = match *self {
224 GraphicalModel::CRF1D => "crf1d",
225 };
226 write!(f, "{}", desc)
227 }
228}
229
230impl ::std::str::FromStr for GraphicalModel {
231 type Err = CrfError;
232
233 fn from_str(s: &str) -> Result<Self> {
234 match s {
235 "1d" | "crf1d" => Ok(GraphicalModel::CRF1D),
236 _ => Err(CrfError::InvalidArgument(s.to_string())),
237 }
238 }
239}
240
241#[derive(Debug)]
245pub struct Trainer {
246 data: *mut crfsuite_data_t,
247 trainer: *mut crfsuite_trainer_t,
248 #[allow(dead_code)]
249 verbose: bool,
250}
251
252impl Default for Trainer {
253 fn default() -> Self {
254 Trainer::new(false)
255 }
256}
257
258#[cfg(not(windows))]
259extern "C" {
260 fn vsnprintf(buf: *mut c_char, size: c_uint, fmt: *const c_char, va_list: *mut c_void);
261}
262
263#[cfg(not(windows))]
264extern "C" fn logging_callback(
265 user: *mut c_void,
266 format: *const c_char,
267 args: *mut __va_list_tag,
268) -> c_int {
269 let trainer: &Trainer = unsafe { mem::transmute(user) };
270 if !trainer.verbose {
271 return 0;
272 }
273 unsafe {
274 let mut buf = mem::MaybeUninit::<[c_char; 65535]>::uninit();
275 let buf = {
276 vsnprintf(buf.as_mut_ptr() as _, 65534, format, mem::transmute(args));
277 buf.assume_init()
278 };
279 let message = CStr::from_ptr(buf.as_ptr()).to_str().unwrap();
280 print!("{}", message);
281 }
282 0
283}
284
285impl Trainer {
286 pub fn new(verbose: bool) -> Self {
288 unsafe {
289 let data_ptr = libc::malloc(mem::size_of::<crfsuite_data_t>()) as *mut crfsuite_data_t;
290 if !data_ptr.is_null() {
291 crfsuite_data_init(data_ptr);
292 }
293 Self {
294 data: data_ptr,
295 trainer: ptr::null_mut(),
296 verbose,
297 }
298 }
299 }
300
301 fn init(&mut self) -> Result<()> {
302 unsafe {
303 let interface = CString::new("dictionary").unwrap();
304 if (*self.data).labels.is_null() {
305 let ret = crfsuite_create_instance(
306 interface.as_ptr() as *const _,
307 &mut (*self.data).attrs as *mut *mut _ as *mut *mut _,
308 );
309 if ret == 0 {
311 return Err(CrfError::CreateInstanceError(
312 "Failed to create a dictionary instance for attributes.".to_string(),
313 ));
314 }
315 }
316 if (*self.data).labels.is_null() {
317 let ret = crfsuite_create_instance(
318 interface.as_ptr() as *const _,
319 &mut (*self.data).labels as *mut *mut _ as *mut *mut _,
320 );
321 if ret == 0 {
323 return Err(CrfError::CreateInstanceError(
324 "Failed to create a dictionary instance for labels.".to_string(),
325 ));
326 }
327 }
328 }
329 #[cfg(not(windows))]
330 {
331 self.set_message_callback();
332 }
333 Ok(())
334 }
335
336 pub fn clear(&mut self) -> Result<()> {
338 if self.data.is_null() {
339 return Ok(());
340 }
341 unsafe {
342 if !(*self.data).attrs.is_null() {
343 (*(*self.data).attrs)
344 .release
345 .map(|release| release((*self.data).attrs))
346 .unwrap();
347 (*self.data).attrs = ptr::null_mut();
348 }
349 if !(*self.data).labels.is_null() {
350 (*(*self.data).labels)
351 .release
352 .map(|release| release((*self.data).labels))
353 .unwrap();
354 (*self.data).labels = ptr::null_mut();
355 }
356 crfsuite_data_finish(self.data);
357 crfsuite_data_init(self.data);
358 }
359 Ok(())
360 }
361
362 pub fn append<T: AsRef<str>>(&mut self, xseq: &[Item], yseq: &[T], group: i32) -> Result<()> {
373 unsafe {
374 if (*self.data).attrs.is_null() || (*self.data).labels.is_null() {
375 self.init()?;
376 }
377 let xseq_len = xseq.len();
378 assert_eq!(xseq_len, yseq.len());
379 let mut instance = mem::MaybeUninit::<crfsuite_instance_t>::uninit();
380 let mut instance = {
381 crfsuite_instance_init_n(instance.as_mut_ptr(), xseq_len as i32);
382 instance.assume_init()
383 };
384 let crf_items = slice::from_raw_parts_mut(instance.items, instance.num_items as usize);
385 let crf_labels =
386 slice::from_raw_parts_mut(instance.labels, instance.num_items as usize);
387 for t in 0..xseq_len {
388 let items = &xseq[t];
389 let crf_item = &mut crf_items[t];
390 crfsuite_item_init_n(crf_item, items.len() as i32);
392 let contents =
393 slice::from_raw_parts_mut(crf_item.contents, crf_item.num_contents as usize);
394 for (content, item) in contents.iter_mut().zip(items) {
395 let name_cstr = CString::new(&item.name[..]).unwrap();
396 let aid = (*(*self.data).attrs)
397 .get
398 .map(|f| f((*self.data).attrs, name_cstr.as_ptr()))
399 .unwrap();
400 (*content).aid = aid;
401 (*content).value = item.value;
402 }
403 let y_value = yseq[t].as_ref();
405 let y_cstr = CString::new(y_value).unwrap();
406 let label = (*(*self.data).labels)
407 .get
408 .map(|f| f((*self.data).labels, y_cstr.as_ptr()))
409 .unwrap();
410 crf_labels[t] = label;
411 }
412 instance.group = group;
413 crfsuite_data_append(self.data, &instance);
415 crfsuite_instance_finish(&mut instance);
417 }
418 Ok(())
419 }
420
421 pub fn select(&mut self, algorithm: Algorithm, typ: GraphicalModel) -> Result<()> {
423 unsafe {
424 if !self.trainer.is_null() {
426 (*self.trainer).release.map(|f| f(self.trainer)).unwrap();
427 self.trainer = ptr::null_mut();
428 }
429 let mut tid = String::from("train/");
430 tid.push_str(&typ.to_string());
431 tid.push_str("/");
432 tid.push_str(&algorithm.to_string());
433 let tid_cstr = CString::new(tid).unwrap();
434 let ret = crfsuite_create_instance(
435 tid_cstr.as_ptr(),
436 &mut self.trainer as *mut *mut _ as *mut *mut _,
437 );
438 if ret == 0 {
440 return Err(CrfError::CreateInstanceError(
441 "Failed to create a instance for trainer.".to_string(),
442 ));
443 }
444 }
445 Ok(())
446 }
447
448 pub fn train(&mut self, model: &str, holdout: i32) -> Result<()> {
462 if self.trainer.is_null() {
463 return Err(CrfError::AlgorithmNotSelected);
464 }
465 unsafe {
466 if (*self.data).attrs.is_null() || (*self.data).labels.is_null() {
467 return Err(CrfError::EmptyData);
468 }
469 let model_cstr = CString::new(model).unwrap();
470 let ret = (*self.trainer)
471 .train
472 .map(|f| f(self.trainer, self.data, model_cstr.as_ptr(), holdout))
473 .unwrap();
474 if ret != 0 {
475 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
476 }
477 }
478 Ok(())
479 }
480
481 pub fn params(&self) -> Vec<String> {
486 unsafe {
487 let pms = (*self.trainer).params.map(|f| f(self.trainer)).unwrap();
488 let n = (*pms).num.map(|f| f(pms)).unwrap();
489 let mut ret = Vec::with_capacity(n as usize);
490 for i in 0..n {
491 let mut name: *mut libc::c_char = ptr::null_mut();
492 (*pms).name.map(|f| f(pms, i, &mut name)).unwrap();
493 let c_str = CStr::from_ptr(name);
494 ret.push(c_str.to_string_lossy().into_owned());
495 (*pms).free.map(|f| f(pms, name)).unwrap();
496 }
497 (*pms).release.map(|f| f(pms)).unwrap();
498 ret
499 }
500 }
501
502 pub fn set(&mut self, name: &str, value: &str) -> Result<()> {
507 let name_cstr = CString::new(name).unwrap();
508 let value_cstr = CString::new(value).unwrap();
509 unsafe {
510 let pms = (*self.trainer).params.map(|f| f(self.trainer)).unwrap();
511 if (*pms)
512 .set
513 .map(|f| f(pms, name_cstr.as_ptr(), value_cstr.as_ptr()))
514 .unwrap()
515 != 0
516 {
517 (*pms).release.map(|f| f(pms)).unwrap();
518 return Err(CrfError::ParamNotFound(name.to_string()));
519 }
520 (*pms).release.map(|f| f(pms)).unwrap();
521 }
522 Ok(())
523 }
524
525 pub fn get(&self, name: &str) -> Result<String> {
530 let name_cstr = CString::new(name).unwrap();
531 let value;
532 unsafe {
533 let mut value_ptr: *mut libc::c_char = ptr::null_mut();
534 let pms = (*self.trainer).params.map(|f| f(self.trainer)).unwrap();
535 if (*pms)
536 .get
537 .map(|f| f(pms, name_cstr.as_ptr(), &mut value_ptr))
538 .unwrap()
539 != 0
540 {
541 (*pms).release.map(|f| f(pms)).unwrap();
542 return Err(CrfError::ParamNotFound(name.to_string()));
543 }
544 value = CStr::from_ptr(value_ptr).to_string_lossy().into_owned();
545 (*pms).free.map(|f| f(pms, value_ptr)).unwrap();
546 (*pms).release.map(|f| f(pms)).unwrap();
547 }
548 Ok(value)
549 }
550
551 pub fn help(&self, name: &str) -> Result<String> {
557 let name_cstr = CString::new(name).unwrap();
558 let value;
559 unsafe {
560 let mut value_ptr: *mut libc::c_char = ptr::null_mut();
561 let pms = (*self.trainer).params.map(|f| f(self.trainer)).unwrap();
562 if (*pms)
563 .help
564 .map(|f| f(pms, name_cstr.as_ptr(), ptr::null_mut(), &mut value_ptr))
565 .unwrap()
566 != 0
567 {
568 (*pms).release.map(|f| f(pms)).unwrap();
569 return Err(CrfError::ParamNotFound(name.to_string()));
570 }
571 value = CStr::from_ptr(value_ptr).to_string_lossy().into_owned();
572 (*pms).free.map(|f| f(pms, value_ptr)).unwrap();
573 (*pms).release.map(|f| f(pms)).unwrap();
574 }
575 Ok(value)
576 }
577
578 #[cfg(not(windows))]
579 fn set_message_callback(&mut self) {
582 unsafe {
583 (*self.trainer)
584 .set_message_callback
585 .map(|f| f(self.trainer, mem::transmute(self), Some(logging_callback)))
586 .unwrap();
587 }
588 }
589}
590
591impl Drop for Trainer {
592 fn drop(&mut self) {
593 unsafe {
594 if !self.data.is_null() {
595 self.clear().unwrap();
596 libc::free(self.data as *mut _);
597 self.data = ptr::null_mut();
598 }
599 if !self.trainer.is_null() {
600 (*self.trainer).release.map(|f| f(self.trainer)).unwrap();
601 self.trainer = ptr::null_mut();
602 }
603 }
604 }
605}
606
607#[derive(Debug)]
609pub struct Model(*mut crfsuite_model_t);
610
611#[derive(Debug)]
614pub struct Tagger<'a> {
615 model: &'a Model,
616 tagger: *mut crfsuite_tagger_t,
617}
618
619impl Model {
620 #[inline]
621 fn new() -> Self {
622 Model(ptr::null_mut())
623 }
624
625 pub fn from_file(name: &str) -> Result<Self> {
627 let mut file = File::open(name)
628 .map_err(|err| CrfError::InvalidModel(format!("Failed to open model: {}", err)))?;
629 Self::validate_model(&mut file)?;
630 drop(file); let mut model = Model::new();
633 model.open(name)?;
634 Ok(model)
635 }
636
637 pub fn from_memory(bytes: &[u8]) -> Result<Self> {
639 let mut cdr = Cursor::new(bytes);
640 Self::validate_model(&mut cdr)?;
641 let mut instance = ptr::null_mut();
642 unsafe {
643 let ret = crfsuite_create_instance_from_memory(
644 bytes.as_ptr() as *const c_void,
645 bytes.len(),
646 &mut instance,
647 );
648 if ret != 0 {
649 return Err(CrfError::CreateInstanceError(
650 "Failed to create a model instance.".to_string(),
651 ));
652 }
653 }
654 let model: *mut crfsuite_sys::crfsuite_model_t = unsafe { mem::transmute(instance) };
655 Ok(Model(model))
656 }
657
658 fn validate_model<R: Read + Seek>(reader: &mut R) -> Result<()> {
662 let mut magic = [0; 4];
664 reader.read_exact(&mut magic).map_err(|err| {
665 CrfError::InvalidModel(format!("Failed to read model file magic: {}", err))
666 })?;
667 if &magic != b"lCRF" {
668 return Err(CrfError::InvalidModel(
669 "Invalid model file magic".to_string(),
670 ));
671 }
672 let pos = reader
674 .seek(SeekFrom::End(0))
675 .map_err(|err| CrfError::InvalidModel(format!("Invalid model: {}", err)))?;
676 if pos <= 48 {
677 return Err(CrfError::InvalidModel(
679 "Invalid model file header".to_string(),
680 ));
681 }
682 Ok(())
683 }
684
685 fn open(&mut self, name: &str) -> Result<()> {
687 let name_cstr = CString::new(name).unwrap();
688 unsafe {
689 let ret = crfsuite_create_instance_from_file(
690 name_cstr.as_ptr(),
691 &mut self.0 as *mut *mut _ as *mut *mut _,
692 );
693 if ret != 0 {
694 return Err(CrfError::CreateInstanceError(
695 "Failed to create a model instance.".to_string(),
696 ));
697 }
698 }
699 Ok(())
700 }
701
702 fn close(&mut self) {
704 unsafe {
705 if !self.0.is_null() {
706 (*self.0).release.map(|f| f(self.0)).unwrap();
707 }
708 }
709 }
710
711 pub fn tagger(&self) -> Result<Tagger> {
712 unsafe {
713 let mut tagger = ptr::null_mut();
714 let ret = (*self.0)
715 .get_tagger
716 .map(|f| f(self.0, &mut tagger))
717 .unwrap();
718 if ret != 0 {
719 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
720 }
721 Ok(Tagger {
722 model: self,
723 tagger,
724 })
725 }
726 }
727
728 #[cfg(unix)]
729 pub fn dump(&self, fd: RawFd) -> Result<()> {
736 let c_mode = CString::new("w").unwrap();
737 unsafe {
738 let file = fdopen(fd, c_mode.as_ptr());
739 if file.is_null() {
740 panic!("fdopen failed");
741 }
742 let ret = (*self.0).dump.map(|f| f(self.0, file)).unwrap();
743 if ret != 0 {
744 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
745 }
746 fclose(file);
747 }
748 Ok(())
749 }
750
751 #[cfg(windows)]
752 pub fn dump(&self, fd: RawHandle) -> Result<()> {
759 unsafe {
760 let fd = libc::open_osfhandle(fd as _, libc::O_RDWR);
761 if fd == -1 {
762 panic!("open_osfhandle failed");
763 }
764 let c_mode = CString::new("w").unwrap();
765 let file = fdopen(fd, c_mode.as_ptr());
766 if file.is_null() {
767 panic!("fdopen failed");
768 }
769 let ret = (*self.0).dump.map(|f| f(self.0, file)).unwrap();
770 if ret != 0 {
771 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
772 }
773 fclose(file);
774 }
775 Ok(())
776 }
777
778 #[cfg(unix)]
779 pub fn dump_file<T: AsRef<Path>>(&self, path: T) -> Result<()> {
786 let file = File::create(path).expect("create file failed");
787 self.dump(file.into_raw_fd())
788 }
789
790 #[cfg(windows)]
791 pub fn dump_file<T: AsRef<Path>>(&self, path: T) -> Result<()> {
798 let file = File::create(path).expect("create file failed");
799 self.dump(file.into_raw_handle())
800 }
801
802 unsafe fn get_attrs(&self) -> Result<*mut crfsuite_dictionary_t> {
803 let mut attrs: *mut crfsuite_dictionary_t = ptr::null_mut();
804 let ret = (*self.0).get_attrs.map(|f| f(self.0, &mut attrs)).unwrap();
805 if ret != 0 {
806 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
807 }
808 Ok(attrs)
809 }
810
811 unsafe fn get_labels(&self) -> Result<*mut crfsuite_dictionary_t> {
812 let mut labels: *mut crfsuite_dictionary_t = ptr::null_mut();
813 let ret = (*self.0)
814 .get_labels
815 .map(|f| f(self.0, &mut labels))
816 .unwrap();
817 if ret != 0 {
818 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
819 }
820 Ok(labels)
821 }
822}
823
824impl Drop for Model {
825 fn drop(&mut self) {
826 self.close();
827 }
828}
829
830unsafe impl Send for Model {}
831unsafe impl Sync for Model {}
832
833impl<'a> Drop for Tagger<'a> {
834 fn drop(&mut self) {
835 unsafe {
836 (*self.tagger).release.map(|f| f(self.tagger)).unwrap();
837 }
838 }
839}
840
841impl<'a> Tagger<'a> {
842 pub fn labels(&self) -> Result<Vec<String>> {
844 unsafe {
845 let labels = self.model.get_labels()?;
846 let length = (*labels).num.map(|f| f(labels)).unwrap();
847 let mut lseq = Vec::with_capacity(length as usize);
848 for i in 0..length {
849 let mut label: *mut libc::c_char = ptr::null_mut();
850 let ret = (*labels)
851 .to_string
852 .map(|f| f(labels, i, &mut label as *mut *mut _ as *mut *const _))
853 .unwrap();
854 if ret != 0 {
855 (*labels).release.map(|f| f(labels)).unwrap();
856 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
857 }
858 lseq.push(CStr::from_ptr(label).to_string_lossy().into_owned());
859 (*labels).free.map(|f| f(labels, label)).unwrap();
860 }
861 (*labels).release.map(|f| f(labels)).unwrap();
862 Ok(lseq)
863 }
864 }
865
866 pub fn tag(&mut self, xseq: &[Item]) -> Result<Vec<String>> {
868 self.set(xseq)?;
869 self.viterbi()
870 }
871
872 fn set(&mut self, xseq: &[Item]) -> Result<()> {
874 unsafe {
875 let attrs = self.model.get_attrs()?;
876 let xseq_len = xseq.len();
877 let mut instance = mem::MaybeUninit::<crfsuite_instance_t>::uninit();
878 let mut instance = {
879 crfsuite_instance_init_n(instance.as_mut_ptr(), xseq_len as i32);
880 instance.assume_init()
881 };
882 let crf_items = slice::from_raw_parts_mut(instance.items, instance.num_items as usize);
883 for t in 0..xseq_len {
884 let items = &xseq[t];
885 let crf_item = &mut crf_items[t];
886 crfsuite_item_init(crf_item);
888 for attr in items.iter() {
889 let name_cstr = CString::new(&attr.name[..]).unwrap();
890 let aid = (*attrs)
891 .to_id
892 .map(|f| f(attrs, name_cstr.as_ptr()))
893 .unwrap();
894 if aid >= 0 {
895 let mut cont = mem::MaybeUninit::<crfsuite_attribute_t>::uninit();
896 let cont = {
897 crfsuite_attribute_set(cont.as_mut_ptr(), aid, attr.value);
898 cont.assume_init()
899 };
900 crfsuite_item_append_attribute(crf_item, &cont);
901 }
902 }
903 }
904
905 let ret = (*self.tagger)
907 .set
908 .map(|f| f(self.tagger, &mut instance))
909 .unwrap();
910 if ret != 0 {
911 (*attrs).release.map(|f| f(attrs)).unwrap();
912 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
913 }
914 crfsuite_instance_finish(&mut instance);
915 (*attrs).release.map(|f| f(attrs)).unwrap();
916 }
917 Ok(())
918 }
919
920 pub fn viterbi(&self) -> Result<Vec<String>> {
922 unsafe {
923 let length = (*self.tagger).length.map(|f| f(self.tagger)).unwrap();
925 if length <= 0 {
926 return Ok(Vec::new());
927 }
928 let labels = self.model.get_labels()?;
929 let mut score: floatval_t = 0.0;
931 let mut paths: Vec<libc::c_int> = Vec::with_capacity(length as usize);
932 let ret = (*self.tagger)
933 .viterbi
934 .map(|f| f(self.tagger, paths.as_mut_ptr(), &mut score))
935 .unwrap();
936 if ret != 0 {
937 (*labels).release.map(|f| f(labels)).unwrap();
938 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
939 }
940 paths.set_len(length as usize);
941 let mut yseq = Vec::with_capacity(length as usize);
942 for path in paths {
944 let mut label: *mut libc::c_char = ptr::null_mut();
945 let ret = (*labels)
946 .to_string
947 .map(|f| f(labels, path, &mut label as *mut *mut _ as *mut *const _))
948 .unwrap();
949 if ret != 0 {
950 (*labels).release.map(|f| f(labels)).unwrap();
951 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
952 }
953 yseq.push(CStr::from_ptr(label).to_string_lossy().into_owned());
954 (*labels).free.map(|f| f(labels, label)).unwrap();
955 }
956 (*labels).release.map(|f| f(labels)).unwrap();
957 Ok(yseq)
958 }
959 }
960
961 pub fn probability<T: AsRef<str>>(&self, yseq: &[T]) -> Result<f64> {
963 let mut score: floatval_t = 0.0;
964 unsafe {
965 let length = (*self.tagger).length.map(|f| f(self.tagger)).unwrap() as usize;
967 if length == 0 {
968 return Ok(score);
969 }
970 if length != yseq.len() {
972 return Err(CrfError::InvalidArgument(format!(
973 "The numbers of items and labels differ: |x| = {}, |y| = {}",
974 length,
975 yseq.len()
976 )));
977 }
978 let labels = self.model.get_labels()?;
980 let mut paths: Vec<libc::c_int> = Vec::with_capacity(length);
982 for y in yseq.iter() {
983 let y_cstr = CString::new(y.as_ref()).unwrap();
984 let l = (*labels).to_id.map(|f| f(labels, y_cstr.as_ptr())).unwrap();
985 if l < 0 {
986 (*labels).release.map(|f| f(labels)).unwrap();
987 return Err(CrfError::ValueError(format!(
988 "Failed to convert into label identifier: {}",
989 y.as_ref()
990 )));
991 }
992 paths.push(l);
993 }
994 let ret = (*self.tagger)
996 .score
997 .map(|f| f(self.tagger, paths.as_mut_ptr(), &mut score))
998 .unwrap();
999 if ret != 0 {
1000 (*labels).release.map(|f| f(labels)).unwrap();
1001 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
1002 }
1003 let mut lognorm: floatval_t = 0.0;
1005 let ret = (*self.tagger)
1006 .lognorm
1007 .map(|f| f(self.tagger, &mut lognorm))
1008 .unwrap();
1009 (*labels).release.map(|f| f(labels)).unwrap();
1010 if ret != 0 {
1011 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
1012 }
1013 Ok((score - lognorm).exp())
1014 }
1015 }
1016
1017 pub fn marginal(&self, label: &str, position: i32) -> Result<f64> {
1019 let mut prob: floatval_t = 0.0;
1020 unsafe {
1021 let length = (*self.tagger).length.map(|f| f(self.tagger)).unwrap() as usize;
1023 if length == 0 {
1024 return Ok(prob);
1025 }
1026 if position < 0 || length <= position as usize {
1028 return Err(CrfError::InvalidArgument(format!(
1029 "The position {} is out of range of {}",
1030 position, length
1031 )));
1032 }
1033 let labels = self.model.get_labels()?;
1035 let y_cstr = CString::new(label).unwrap();
1037 let l = (*labels).to_id.map(|f| f(labels, y_cstr.as_ptr())).unwrap();
1038 if l < 0 {
1039 (*labels).release.map(|f| f(labels)).unwrap();
1040 return Err(CrfError::ValueError(format!(
1041 "Failed to convert into label identifier: {}",
1042 label
1043 )));
1044 }
1045 let ret = (*self.tagger)
1047 .marginal_point
1048 .map(|f| f(self.tagger, l, position, &mut prob))
1049 .unwrap();
1050 (*labels).release.map(|f| f(labels)).unwrap();
1051 if ret != 0 {
1052 return Err(CrfError::CrfSuiteError(CrfSuiteError::from(ret)));
1053 }
1054 Ok(prob)
1055 }
1056 }
1057}
1058
1059#[cfg(test)]
1060mod tests {
1061 use super::{Algorithm, Attribute, GraphicalModel, Result};
1062
1063 #[test]
1064 fn test_str_to_algorithm_enum() {
1065 let algo: Algorithm = "lbfgs".parse().unwrap();
1066 assert_eq!(algo, Algorithm::LBFGS);
1067
1068 let algo: Algorithm = "l2sgd".parse().unwrap();
1069 assert_eq!(algo, Algorithm::L2SGD);
1070
1071 let algo: Algorithm = "ap".parse().unwrap();
1072 assert_eq!(algo, Algorithm::AP);
1073 let algo: Algorithm = "averaged-perceptron".parse().unwrap();
1074 assert_eq!(algo, Algorithm::AP);
1075
1076 let algo: Algorithm = "pa".parse().unwrap();
1077 assert_eq!(algo, Algorithm::PA);
1078 let algo: Algorithm = "passive-aggressive".parse().unwrap();
1079 assert_eq!(algo, Algorithm::PA);
1080
1081 let algo: Algorithm = "arow".parse().unwrap();
1082 assert_eq!(algo, Algorithm::AROW);
1083
1084 let algo: Result<Algorithm> = "foo".parse();
1085 assert!(algo.is_err());
1086 }
1087
1088 #[test]
1089 fn test_algorithm_enum_to_str() {
1090 assert_eq!("lbfgs", &Algorithm::LBFGS.to_string());
1091 assert_eq!("l2sgd", &Algorithm::L2SGD.to_string());
1092 assert_eq!("averaged-perceptron", &Algorithm::AP.to_string());
1093 assert_eq!("passive-aggressive", &Algorithm::PA.to_string());
1094 assert_eq!("arow", &Algorithm::AROW.to_string());
1095 }
1096
1097 #[test]
1098 fn test_str_to_graphical_model_enum() {
1099 let model: GraphicalModel = "1d".parse().unwrap();
1100 assert_eq!(model, GraphicalModel::CRF1D);
1101 let model: GraphicalModel = "crf1d".parse().unwrap();
1102 assert_eq!(model, GraphicalModel::CRF1D);
1103
1104 let model: Result<GraphicalModel> = "foo".parse();
1105 assert!(model.is_err());
1106 }
1107
1108 #[test]
1109 fn test_attribute() {
1110 Attribute::new("foo", 1.0);
1111 Attribute::from(("foo", 1.0));
1112 assert_eq!(Attribute::from("foo"), Attribute::from(("foo", 1.0)));
1113 }
1114}