use crate::fst::Label;
use crate::semiring::gallic::variant::GallicVariant;
use crate::semiring::{Semiring, SemiringProperties};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RestrictGallic;
impl GallicVariant for RestrictGallic {
fn plus<W: Semiring>(
labels1: &[Label],
weight1: &W,
labels2: &[Label],
weight2: &W,
) -> (Vec<Label>, W) {
if labels1 == labels2 {
let combined_weight = weight1.plus(weight2);
(labels1.to_vec(), combined_weight)
} else {
(Vec::new(), W::zero())
}
}
fn variant_name() -> &'static str {
"Restrict"
}
fn properties<W: Semiring>() -> SemiringProperties {
let weight_props = W::properties();
SemiringProperties {
left_semiring: true,
right_semiring: true,
commutative: weight_props.commutative,
idempotent: weight_props.idempotent,
path: weight_props.path,
}
}
fn is_functional() -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::semiring::{ProbabilityWeight, TropicalWeight};
use num_traits::Zero;
#[test]
fn test_restrict_gallic_plus_identical_labels() {
let labels1 = vec![1, 2, 3];
let labels2 = vec![1, 2, 3];
let w1 = TropicalWeight::new(1.0);
let w2 = TropicalWeight::new(2.0);
let (result_labels, result_weight) = RestrictGallic::plus(&labels1, &w1, &labels2, &w2);
assert_eq!(result_labels, vec![1, 2, 3]);
assert_eq!(*result_weight.value(), 1.0); assert!(!Semiring::is_zero(&result_weight));
}
#[test]
fn test_restrict_gallic_plus_different_labels() {
let labels1 = vec![1, 2, 3];
let labels2 = vec![1, 2, 4];
let w1 = TropicalWeight::new(1.0);
let w2 = TropicalWeight::new(2.0);
let (result_labels, result_weight) = RestrictGallic::plus(&labels1, &w1, &labels2, &w2);
assert_eq!(result_labels, Vec::<Label>::new());
assert!(Semiring::is_zero(&result_weight)); }
#[test]
fn test_restrict_gallic_plus_empty_vs_nonempty() {
let labels1: Vec<Label> = vec![];
let labels2 = vec![1, 2];
let w1 = TropicalWeight::new(1.0);
let w2 = TropicalWeight::new(2.0);
let (result_labels, result_weight) = RestrictGallic::plus(&labels1, &w1, &labels2, &w2);
assert_eq!(result_labels, Vec::<Label>::new());
assert!(Semiring::is_zero(&result_weight));
}
#[test]
fn test_restrict_gallic_plus_both_empty() {
let labels1: Vec<Label> = vec![];
let labels2: Vec<Label> = vec![];
let w1 = TropicalWeight::new(1.0);
let w2 = TropicalWeight::new(2.0);
let (result_labels, result_weight) = RestrictGallic::plus(&labels1, &w1, &labels2, &w2);
assert_eq!(result_labels, Vec::<Label>::new());
assert_eq!(*result_weight.value(), 1.0);
assert!(!Semiring::is_zero(&result_weight));
}
#[test]
fn test_restrict_gallic_plus_different_lengths() {
let labels1 = vec![1, 2];
let labels2 = vec![1, 2, 3];
let w1 = TropicalWeight::new(1.0);
let w2 = TropicalWeight::new(2.0);
let (result_labels, result_weight) = RestrictGallic::plus(&labels1, &w1, &labels2, &w2);
assert_eq!(result_labels, Vec::<Label>::new());
assert!(Semiring::is_zero(&result_weight));
}
#[test]
fn test_restrict_gallic_plus_probability_weights() {
let labels1 = vec![5, 6, 7];
let labels2 = vec![5, 6, 7];
let w1 = ProbabilityWeight::new(0.3);
let w2 = ProbabilityWeight::new(0.5);
let (result_labels, result_weight) = RestrictGallic::plus(&labels1, &w1, &labels2, &w2);
assert_eq!(result_labels, vec![5, 6, 7]);
assert_eq!(*result_weight.value(), 0.8); assert!(!Semiring::is_zero(&result_weight));
}
#[test]
fn test_restrict_gallic_enforces_functional() {
let labels1 = vec![1, 2, 3];
let labels2 = vec![1, 2, 3];
let labels3 = vec![1, 2, 4]; let w = TropicalWeight::new(1.0);
let (_, weight_valid) = RestrictGallic::plus(&labels1, &w, &labels2, &w);
assert!(!Semiring::is_zero(&weight_valid));
let (_, weight_invalid) = RestrictGallic::plus(&labels1, &w, &labels3, &w);
assert!(Semiring::is_zero(&weight_invalid));
}
#[test]
fn test_restrict_gallic_variant_name() {
assert_eq!(RestrictGallic::variant_name(), "Restrict");
}
#[test]
fn test_restrict_gallic_properties() {
let props = RestrictGallic::properties::<TropicalWeight>();
assert!(props.left_semiring);
assert!(props.right_semiring);
assert!(props.commutative);
assert!(props.idempotent);
}
#[test]
fn test_restrict_gallic_is_functional() {
assert!(RestrictGallic::is_functional());
}
#[test]
fn test_restrict_gallic_commutative_when_matched() {
let labels1 = vec![1, 2, 3];
let labels2 = vec![1, 2, 3];
let w1 = TropicalWeight::new(5.0);
let w2 = TropicalWeight::new(3.0);
let (result1, weight1) = RestrictGallic::plus(&labels1, &w1, &labels2, &w2);
let (result2, weight2) = RestrictGallic::plus(&labels2, &w2, &labels1, &w1);
assert_eq!(result1, result2);
assert_eq!(weight1, weight2);
}
#[test]
fn test_restrict_gallic_zero_detection() {
let labels1 = vec![1];
let labels2 = vec![2];
let w = TropicalWeight::new(1.0);
let (labels, weight) = RestrictGallic::plus(&labels1, &w, &labels2, &w);
assert!(labels.is_empty());
assert!(Semiring::is_zero(&weight));
assert_eq!(weight, TropicalWeight::zero());
}
}