1use std::iter::Sum;
2use std::ops::AddAssign;
3
4use num::Float;
5
6use crate::math::interp;
7
8#[derive(Clone, Copy, Debug)]
11pub enum AndOp {
12 Min,
13 Prod,
14 BoundedProd,
15 DrasticProd,
16}
17
18impl AndOp {
19 pub fn call<F: Float>(
20 self,
21 u: impl IntoIterator<Item = F>,
22 v: impl IntoIterator<Item = F>,
23 ) -> impl IntoIterator<Item = F> {
24 match self {
25 Self::Min => ProductionLink::Min.call(u, v),
26 Self::Prod => ProductionLink::Prod.call(u, v),
27 Self::BoundedProd => ProductionLink::BoundedProd.call(u, v),
28 Self::DrasticProd => ProductionLink::DrasticProd.call(u, v),
29 }
30 }
31}
32
33#[derive(Clone, Copy, Debug)]
36pub enum OrOp {
37 Max,
38 ProbOr,
39 BoundedSum,
40 DrasticSum,
41}
42
43impl OrOp {
44 pub fn call<F: Float>(
45 self,
46 u: impl IntoIterator<Item = F>,
47 v: impl IntoIterator<Item = F>,
48 ) -> impl IntoIterator<Item = F> {
49 match self {
50 Self::Max => ProductionLink::Max.call(u, v),
51 Self::ProbOr => ProductionLink::ProbOr.call(u, v),
52 Self::BoundedSum => ProductionLink::BoundedSum.call(u, v),
53 Self::DrasticSum => ProductionLink::DrasticSum.call(u, v),
54 }
55 }
56}
57
58pub enum CompositionOp {
59 MaxMin,
60 MaxProd,
61}
62
63#[derive(Clone, Copy, Debug)]
66pub enum ImplicationOp {
67 Ra,
68 Rm,
69 Rc,
70 Rb,
71 Rs,
72 Rg,
73 Rsg,
74 Rgs,
75 Rgg,
76 Rss,
77}
78
79impl ImplicationOp {
80 pub fn call<F: Float>(
81 self,
82 u: impl IntoIterator<Item = F>,
83 v: impl IntoIterator<Item = F>,
84 ) -> impl IntoIterator<Item = F> {
85 u.into_iter().zip(v.into_iter()).map(move |(u, v)| match self {
86 Self::Ra => F::min(F::one(), F::one() - u + v),
87 Self::Rm => F::max(F::min(u, v), F::one() - u),
88 Self::Rc => F::min(u, v),
89 Self::Rb => F::max(F::one() - u, v),
90 Self::Rs => {
91 if u <= v {
92 F::one()
93 } else {
94 F::zero()
95 }
96 },
97 Self::Rg => {
98 if u <= v {
99 F::one()
100 } else {
101 v
102 }
103 },
104 Self::Rsg => F::min(
105 Self::Rs.call(Some(u), Some(v)).into_iter().next().expect("unreachable"),
106 Self::Rg
107 .call(Some(F::one() - u), Some(F::one() - v))
108 .into_iter()
109 .next()
110 .expect("unreachable"),
111 ),
112 Self::Rgs => F::min(
113 Self::Rg.call(Some(u), Some(v)).into_iter().next().expect("unreachable"),
114 Self::Rs
115 .call(Some(F::one() - u), Some(F::one() - v))
116 .into_iter()
117 .next()
118 .expect("unreachable"),
119 ),
120 Self::Rgg => F::min(
121 Self::Rg.call(Some(u), Some(v)).into_iter().next().expect("unreachable"),
122 Self::Rg
123 .call(Some(F::one() - u), Some(F::one() - v))
124 .into_iter()
125 .next()
126 .expect("unreachable"),
127 ),
128 Self::Rss => F::min(
129 Self::Rs.call(Some(u), Some(v)).into_iter().next().expect("unreachable"),
130 Self::Rs
131 .call(Some(F::one() - u), Some(F::one() - v))
132 .into_iter()
133 .next()
134 .expect("unreachable"),
135 ),
136 })
137 }
138}
139
140#[derive(Clone, Copy, Debug)]
142pub enum ProductionLink {
143 Min,
144 Prod,
145 BoundedProd,
146 DrasticProd,
147 Max,
148 ProbOr,
149 BoundedSum,
150 DrasticSum,
151}
152
153impl ProductionLink {
154 pub fn call<F: Float>(
155 self,
156 u: impl IntoIterator<Item = F>,
157 v: impl IntoIterator<Item = F>,
158 ) -> impl IntoIterator<Item = F> {
159 u.into_iter().zip(v.into_iter()).map(move |(u, v)| match self {
160 Self::Max => F::max(u, v),
161 Self::ProbOr => u + v - u * v,
162 Self::BoundedSum => F::min(F::one(), u + v),
163 Self::DrasticSum => {
164 if v == F::zero() {
165 u
166 } else if u == F::zero() {
167 v
168 } else {
169 F::one()
170 }
171 },
172 Self::Min => F::min(u, v),
173 Self::Prod => u * v,
174 Self::BoundedProd => F::max(F::zero(), u + v - F::one()),
175 Self::DrasticProd => {
176 if v == F::zero() {
177 u
178 } else if u == F::one() {
179 v
180 } else {
181 F::zero()
182 }
183 },
184 })
185 }
186}
187
188#[derive(Clone, Copy, Debug)]
190pub enum DefuzzificationOp {
191 Cog,
193 Boa,
195 Mom,
197 Lom,
199 Som,
201}
202
203impl DefuzzificationOp {
204 pub fn call<F: Float + Sum + AddAssign>(self, universe: &[F], membership: &[F]) -> F {
205 match self {
206 Self::Cog => {
207 let n_areas = universe.len() - 1;
208 let mut areas = Vec::with_capacity(n_areas);
209 let mut centroids = Vec::with_capacity(n_areas);
210 let two = F::one() + F::one();
211 let three = two + F::one();
212
213 for i in 0..n_areas {
214 let base = universe[i + 1] - universe[i];
215 let area_rect = F::min(membership[i], membership[i + 1]) * base;
216 let center_rect = universe[i] + base / two;
217 let (area_tria, center_tri) = if membership[i + 1] == membership[i] {
218 (F::zero(), F::zero())
219 } else if membership[i + 1] > membership[i] {
220 (
221 base * F::abs(membership[i + 1] - membership[i]) / two,
222 universe[i] + two / three * base,
223 )
224 } else {
225 (
226 base * F::abs(membership[i + 1] - membership[i]) / two,
227 universe[i] + F::one() / three * base,
228 )
229 };
230 let area = area_rect + area_tria;
231 let center = if area == F::zero() {
232 F::zero()
233 } else {
234 (area_rect * center_rect + area_tria * center_tri) / (area_rect + area_tria)
235 };
236
237 areas.push(area);
238 centroids.push(center);
239 }
240
241 let den = areas.iter().copied().sum::<F>();
242 let num = areas
243 .into_iter()
244 .zip(centroids.into_iter())
245 .map(|(area, cent)| area * cent)
246 .sum::<F>();
247
248 num / den
249 },
250 Self::Boa => {
251 let n_areas = universe.len() - 1;
252 let mut areas = Vec::with_capacity(n_areas);
253 let two = F::one() + F::one();
254
255 for i_area in 0..n_areas {
256 let base = universe[i_area + 1] - universe[i_area];
257 let area = (membership[i_area] + membership[i_area + 1]) * base / two;
258 areas.push(area);
259 }
260
261 let total_area = areas.iter().copied().sum::<F>();
262 let target = total_area / two;
263 let mut cum_area = F::zero();
264 let mut i_area = 0;
265
266 for i in 0..=n_areas {
267 cum_area += areas[i];
268 i_area = i;
269 if cum_area >= target {
270 break;
271 }
272 }
273
274 let xp = [universe[i_area], universe[i_area + 1]];
275 let fp = [cum_area - areas[i_area], cum_area];
276
277 interp(Some(target), xp.into_iter().zip(fp.into_iter()))
278 .into_iter()
279 .next()
280 .expect("unreachable")
281 },
282 Self::Mom => {
283 let maximum = membership.iter().copied().reduce(F::max).unwrap();
284 let (len, sum) = universe
285 .iter()
286 .copied()
287 .zip(membership.iter().copied())
288 .filter_map(|(u, m)| if m == maximum { Some(u) } else { None })
289 .enumerate()
290 .fold((0usize, F::zero()), |(_i, accum), (i, next)| (i + 1, accum + next));
291
292 sum / F::from(len).unwrap()
293 },
294 Self::Lom => {
295 let maximum = membership.iter().copied().reduce(F::max).unwrap();
296 universe
297 .iter()
298 .copied()
299 .zip(membership.iter().copied())
300 .filter_map(|(u, m)| if m == maximum { Some(u) } else { None })
301 .reduce(F::max)
302 .unwrap()
303 },
304 Self::Som => {
305 let maximum = membership.iter().copied().reduce(F::max).unwrap();
306 universe
307 .iter()
308 .copied()
309 .zip(membership.iter().copied())
310 .filter_map(|(u, m)| if m == maximum { Some(u) } else { None })
311 .reduce(F::min)
312 .unwrap()
313 },
314 }
315 }
316}