1use crate::strategy::{CITest, CITestDataType, TestResult};
2use crate::utils::power_divergence::power_divergence;
3use ndarray::{Array1, Array2};
4
5const MODIFIED_LIKELIHOOD_LAMBDA: f64 = -1.0;
6
7#[derive(Debug, Clone, PartialEq)]
12pub struct ModifiedLikelihood {
13 pub boolean: bool,
14 pub significance_level: f64,
15}
16
17impl ModifiedLikelihood {
18 #[must_use]
19 pub fn new(boolean: bool, significance_level: f64) -> Self {
20 Self {
21 boolean,
22 significance_level,
23 }
24 }
25}
26
27impl CITest for ModifiedLikelihood {
28 fn run_test(
29 &self,
30 x_values: Array1<f64>,
31 y_values: Array1<f64>,
32 z: Array2<f64>,
33 ) -> anyhow::Result<TestResult> {
34 power_divergence(
35 &x_values,
36 &y_values,
37 &z,
38 self.boolean,
39 self.significance_level,
40 MODIFIED_LIKELIHOOD_LAMBDA,
41 )
42 }
43
44 fn data_types(&self) -> &'static [CITestDataType] {
45 &[CITestDataType::Discrete]
46 }
47}
48
49#[cfg(test)]
50#[allow(clippy::many_single_char_names)]
51mod tests {
52 use super::*;
53 use crate::utils::EPS;
54 use ndarray::{array, Array2};
55
56 fn unwrap_correlated(r: &TestResult) -> (f64, f64, usize) {
57 match r {
58 TestResult::Statistic(a, b, c) => (*a, *b, *c),
59 _ => panic!("expected Correlated2"),
60 }
61 }
62
63 #[test]
64 fn uncond_independent_data_accepted() {
65 let t = ModifiedLikelihood {
66 boolean: false,
67 significance_level: 0.05,
68 };
69 let x = array![1., 1., 2., 2., 1., 1., 2., 2.];
70 let y = array![1., 2., 1., 2., 1., 2., 1., 2.];
71 let empty = Array2::<f64>::zeros((0, 0));
72
73 let (p, stat, dof) = unwrap_correlated(&t.run_test(x, y, empty).unwrap());
74 assert!(stat.abs() < EPS);
75 assert!(p > 0.99);
76 assert_eq!(dof, 1);
77 }
78
79 #[test]
80 fn cond_independent_data_accepted() {
81 let t = ModifiedLikelihood {
82 boolean: false,
83 significance_level: 0.05,
84 };
85 let x = array![1., 1., 2., 2., 1., 1., 2., 2.];
86 let y = array![1., 2., 1., 2., 1., 2., 1., 2.];
87 let z = array![[1.], [1.], [1.], [1.], [2.], [2.], [2.], [2.]];
88
89 let (p, stat, dof) = unwrap_correlated(&t.run_test(x, y, z).unwrap());
90
91 assert!(stat.abs() < EPS, " got stat {stat}");
92 assert!(p > 0.99, " got p {p}");
93 assert_eq!(dof, 2);
94 }
95
96 #[test]
97 fn uncond_dependent_data_rejected() {
98 let t = ModifiedLikelihood {
99 boolean: false,
100 significance_level: 0.05,
101 };
102 let x = array![1., 1., 1., 1., 1., 1., 2., 2., 2., 2., 2., 2.];
103 let y = array![1., 1., 1., 1., 1., 2., 1., 2., 2., 2., 2., 2.];
104 let empty = Array2::<f64>::zeros((0, 0));
105
106 let (p, stat, dof) = unwrap_correlated(&t.run_test(x, y, empty).unwrap());
107 assert!((stat - 7.053_439_978_825_427).abs() < EPS, "got {stat}");
108 assert!((p - 0.007_911_317_670_556_329).abs() < EPS, "got {p}");
109 assert_eq!(dof, 1);
110 }
111
112 #[test]
113 fn cond_dependent_data_rejected() {
114 let t = ModifiedLikelihood {
115 boolean: false,
116 significance_level: 0.05,
117 };
118 let x = array![1., 1., 1., 2., 2., 2., 1., 1., 1., 2., 2., 2.];
119 let y = array![1., 1., 2., 2., 2., 1., 1., 1., 2., 2., 2., 1.];
120 let z = array![
121 [1.],
122 [1.],
123 [1.],
124 [1.],
125 [1.],
126 [1.],
127 [2.],
128 [2.],
129 [2.],
130 [2.],
131 [2.],
132 [2.]
133 ];
134
135 let (p, stat, dof) = unwrap_correlated(&t.run_test(x, y, z).unwrap());
136
137 assert!(
138 (stat - 1.413_396_427_876_601_6).abs() < EPS,
139 "got stat {stat}"
140 );
141 assert!((p - 0.493_270_184_272_571_97).abs() < EPS, "got p {p}");
142 assert_eq!(dof, 2);
143 }
144
145 #[test]
146 fn uncond_bool_rejects_dependent() {
147 let t = ModifiedLikelihood {
148 boolean: true,
149 significance_level: 0.05,
150 };
151 let x = array![1., 1., 1., 1., 1., 1., 2., 2., 2., 2., 2., 2.];
152 let y = array![1., 1., 1., 1., 1., 2., 1., 2., 2., 2., 2., 2.];
153 let empty = Array2::<f64>::zeros((0, 0));
154 let r = t.run_test(x, y, empty).unwrap();
155 assert!(matches!(r, TestResult::Boolean(false)));
156 }
157
158 #[test]
159 fn cond_bool_accepts_independent() {
160 let t = ModifiedLikelihood {
161 boolean: true,
162 significance_level: 0.05,
163 };
164 let x = array![1., 1., 2., 2., 1., 1., 2., 2.];
165 let y = array![1., 1., 2., 2., 1., 1., 2., 2.];
166 let z = array![[1.], [1.], [1.], [1.], [2.], [2.], [2.], [2.]];
167 let r = t.run_test(x, y, z).unwrap();
168 assert!(matches!(r, TestResult::Boolean(false)));
169 }
170}