1use std::{hash::Hash, collections::HashMap, };
18
19#[cfg(feature = "silx-types")] use silx_types::Float;
23
24use crate::{
25 types::{ u32slx, f64slx, SlxInto, IntoSlx, },
26 structs::{Assignment, SafeElement, ASSIGNMENT_EPSILON, one_f64slx, zero_f64slx},
27 traits::Lattice,
28};
29
30use super::ComplementedLattice;
31
32pub trait IterableLattice: Lattice {
34 type IntoIterUp: Iterator<Item = Self::Item>;
36 type IntoIterDown: Iterator<Item = Self::Item>;
38
39 unsafe fn unsafe_bottom_to_top(&self) -> Result<Self::IntoIterUp,String>;
43
44 unsafe fn unsafe_top_to_bottom(&self) -> Result<Self::IntoIterDown,String>;
48
49 fn bottom_to_top(&self) -> Result<std::vec::IntoIter<SafeElement<Self::Item>>,String> {
53 let lattice_hash = self.lattice_hash();
54 Ok(unsafe{ self.unsafe_bottom_to_top() }?.map(
55 move |element| SafeElement { lattice_hash, code: element }
56 ).collect::<Vec<_>>().into_iter())
57 }
58
59 fn top_to_bottom(&self) -> Result<std::vec::IntoIter<SafeElement<Self::Item>>,String> {
63 let lattice_hash = self.lattice_hash();
64 Ok(unsafe{ self.unsafe_top_to_bottom() }?.map(
65 move |element| SafeElement { lattice_hash, code: element }
66 ).collect::<Vec<_>>().into_iter())
67 }
68}
69
70pub trait LatticeWithLeaves: Lattice where Self::Item: Ord + Hash, {
74 type IntoIterLeaves: Iterator<Item = (Self::Item,f64slx)>;
76
77 unsafe fn unsafe_weighted_leaf(&self, u: usize) -> Result<(&Self::Item,&f64slx),String>;
81
82 unsafe fn unsafe_leaves(&self) -> Result<Self::IntoIterLeaves,String>;
85
86 unsafe fn unsafe_leaf(&self, u: usize) -> Result<&Self::Item,String> {
90 Ok(&self.unsafe_weighted_leaf(u)?.0)
91 }
92
93 fn weighted_leaf(&self, u: usize) -> Result<(SafeElement<Self::Item>,f64slx),String> {
97 let (e,w) = unsafe { self.unsafe_weighted_leaf(u)? };
98 let lattice_hash = self.lattice_hash();
99 Ok((SafeElement { code: e.clone(), lattice_hash, },*w))
100 }
101
102 fn leaf(&self, u: usize) -> Result<SafeElement<Self::Item>,String> {
106 let e = unsafe { self.unsafe_leaf(u)? };
107 let lattice_hash = self.lattice_hash();
108 Ok(SafeElement { code: e.clone(), lattice_hash, })
109 }
110
111 fn leaves(&self) -> Result<std::vec::IntoIter<(SafeElement<Self::Item>,f64slx)>,String> {
114 let lattice_hash = self.lattice_hash();
115 Ok(unsafe{ self.unsafe_leaves() }?.map(
116 move |(element,w)| (SafeElement { lattice_hash, code: element },w)
117 ).collect::<Vec<_>>().into_iter())
118 }
119
120 fn mass_to_pignistic(&self, mass: &Assignment<Self::Item>) -> Result<Assignment<Self::Item>,String> {
124 let Assignment { lattice_hash, elements, .. } = mass;
125 if lattice_hash == self.ref_lattice_hash() {
126 let leaves = unsafe { self.unsafe_leaves() }?.collect::<HashMap<_,_>>();
127 let loc_norm = elements.iter().map(|(x,wx)| {
128 let loc_leaves = leaves.iter().filter(
129 |(y,_)|unsafe { self.unsafe_implied_join(x,*y) }
130 ).map(|(y,w)| (y,*w)).collect::<Vec<_>>();
131 let norm = loc_leaves.iter().map(|(_,w)| *w).sum::<f64slx>();
132 (*wx,loc_leaves,norm)
133 }).collect::<Vec<_>>();
134 let mut pign = self.assignment();
135 for (wx,ll,nx) in loc_norm {
136 for (l,w) in ll {
137 unsafe { pign.unsafe_push(l.clone(), wx * w / nx)? };
138 }
139 }
140 pign.normalize()?; Ok(pign.into())
141 } else { Err("Mismatching lattice hash".to_string()) }
142 }
143}
144
145pub trait BeliefTransform: IterableLattice where Self::Item: Ord + Hash, {
147 fn mass_to_commonality<>(&self, mass: &Assignment<Self::Item>) -> Result<Assignment<Self::Item>,String> {
151 let Assignment { lattice_hash, elements, .. } = mass;
152 if lattice_hash == self.ref_lattice_hash() {
153 let vec_com = unsafe{ self.unsafe_top_to_bottom() }?
154 .map(|x| {
155 let mut wx = *zero_f64slx();
156 for (y,wy) in elements {
157 if unsafe{ self.unsafe_implies_join(&x, y) } { wx += *wy; }
158 }
159 (x, wx)
160 }).filter(|(_,w)| w > &zero_f64slx()).collect::<Vec<_>>();
161 let elements = vec_com.clone().into_iter().collect();
162 Ok(Assignment {
163 lattice_hash: *lattice_hash, elements,
164 })
165 } else { Err("Mismatching lattice hash".to_string()) }
166
167 }
168
169 fn mass_from_commonality(&self, commonality: &Assignment<Self::Item>) -> Result<Assignment<Self::Item>,String> {
173 let Assignment { lattice_hash, elements, .. } = commonality;
174 if lattice_hash == self.ref_lattice_hash() {
175 let mut mass = self.assignment();
176 let zero = *zero_f64slx();
177 for x in unsafe{ self.unsafe_top_to_bottom() }? {
178 let mut wx = match elements.get(&x) {
179 Some(w) => *w, None => zero,
180 };
181 for (y,wy) in &mass.elements.elements {
182 if unsafe{ self.unsafe_implies_join(&x, y) } { wx -= *wy; }
183 }
184 if wx.abs() > ASSIGNMENT_EPSILON.slx() { unsafe { mass.unsafe_push(x, wx) }?; }
185 }
186 mass.length_mid = (mass.elements.len() as u32).slx();
187 let slx2u32: u32slx = 2u32.slx();
188 mass.length_max = slx2u32 * mass.length_mid;
189 mass.normalize()?;
190 Ok(mass.into())
191 } else { Err("Mismatching lattice hash".to_string()) }
192 }
193
194 fn mass_to_implicability(&self, mass: &Assignment<Self::Item>) -> Result<Assignment<Self::Item>,String> {
198 let Assignment { lattice_hash, elements, .. } = mass;
199 if lattice_hash == self.ref_lattice_hash() {
200 let vec_com = unsafe{ self.unsafe_bottom_to_top() }?
201 .map(|x| {
202 let mut wx = *zero_f64slx();
203 for (y,wy) in elements {
204 if unsafe{ self.unsafe_implied_join(&x, y) } { wx += *wy; }
205 }
206 (x, wx)
207 }).filter(|(_,w)| w > zero_f64slx()).collect::<Vec<_>>();
208 let elements = vec_com.clone().into_iter().collect();
209 Ok(Assignment { lattice_hash: *lattice_hash, elements, })
210 } else { Err("Mismatching lattice hash".to_string()) }
211 }
212
213 fn mass_from_implicability(&self, implicability: &Assignment<Self::Item>) -> Result<Assignment<Self::Item>,String> {
217 let Assignment { lattice_hash, elements, .. } = implicability;
218 if lattice_hash == self.ref_lattice_hash() {
219 let mut mass = self.assignment();
220 let zero = *zero_f64slx();
221 for x in unsafe{ self.unsafe_bottom_to_top() }? {
222 let mut wx = match elements.get(&x) {
223 Some(w) => *w, None => zero,
224 };
225 for (y,wy) in &mass.elements.elements {
226 if unsafe{ self.unsafe_implied_join(&x, y) } { wx -= *wy; }
227 }
228 if wx.abs() > ASSIGNMENT_EPSILON.slx() { unsafe { mass.unsafe_push(x, wx) }?; }
229 }
230 mass.length_mid = (mass.elements.len() as u32).slx();
231 let slx2u32: u32slx = 2u32.slx();
232 mass.length_max = slx2u32 * mass.length_mid;
233
234 mass.normalize()?;
235 Ok(mass.into())
236 } else { Err("Mismatching lattice hash".to_string()) }
237 }
238
239 fn mass_to_credibility(&self, mass: &Assignment<Self::Item>) -> Result<Assignment<Self::Item>,String> {
243 let Assignment { lattice_hash, elements, .. } = mass;
244 if lattice_hash == self.ref_lattice_hash() {
245 let vec_com = unsafe{ self.unsafe_bottom_to_top() }?
246 .map(|x| {
247 let mut wx = *zero_f64slx();
248 for (y,wy) in elements.iter()
249 .filter(|(x,_)| !unsafe { self.unsafe_is_bottom(*x) }) {
250 if unsafe{ self.unsafe_implied_join(&x, y) } { wx += *wy; }
251 }
252 (x, wx)
253 }).filter(|(_,w)| w > zero_f64slx()).collect::<Vec<_>>();
254 let elements = vec_com.clone().into_iter().collect();
255 Ok(Assignment { lattice_hash: *lattice_hash, elements, })
256 } else { Err("Mismatching lattice hash".to_string()) }
257 }
258
259 fn mass_from_credibility(&self, credibility: &Assignment<Self::Item>) -> Result<Assignment<Self::Item>,String> {
263 let Assignment { lattice_hash, elements, .. } = credibility;
264 if lattice_hash == self.ref_lattice_hash() {
265 let mut mass = self.assignment();
266 let zero = *zero_f64slx();
267 let mut full_w = zero;
268 for x in unsafe{ self.unsafe_bottom_to_top() }? {
269 let mut wx = match elements.get(&x) {
270 Some(w) => *w, None => zero,
271 };
272 for (y,wy) in &mass.elements.elements {
273 if unsafe{ self.unsafe_implied_join(&x, y) } { wx -= *wy; }
274 }
275 if wx.abs() > ASSIGNMENT_EPSILON.slx() {
276 unsafe { mass.unsafe_push(x, wx) }?;
277 full_w += wx;
278 }
279
280 }
281 if full_w > *one_f64slx() { panic!("exceeding weights"); }
282 unsafe { mass.unsafe_push(self.bottom().code, *one_f64slx() - full_w)?; }
283 mass.length_mid = (mass.elements.len() as u32).slx();
284 let slx2u32: u32slx = 2u32.slx();
285 mass.length_max = slx2u32 * mass.length_mid;
286 mass.normalize()?;
287 Ok(mass.into())
288 } else { Err("Mismatching lattice hash".to_string()) }
289 }
290
291 fn mass_to_plausibility(&self, mass: &Assignment<Self::Item>) -> Result<Assignment<Self::Item>,String> {
295 let Assignment { lattice_hash, elements, .. } = mass;
296 if lattice_hash == self.ref_lattice_hash() {
297 let vec_com = unsafe{ self.unsafe_bottom_to_top() }?
298 .map(|x| {
299 let mut wx = *zero_f64slx();
300 for (y,wy) in elements {
301 if !unsafe{ self.unsafe_disjoint(&x, y) } { wx += *wy; }
302 }
303 (x, wx)
304 }).filter(|(_,w)| w > zero_f64slx()).collect::<Vec<_>>();
305 let elements = vec_com.clone().into_iter().collect();
306 Ok(Assignment { lattice_hash: *lattice_hash, elements, })
307 } else { Err("Mismatching lattice hash".to_string()) }
308 }
309
310
311 fn implicability_to_credibility(&self, implicability: &Assignment<Self::Item>) -> Result<Assignment<Self::Item>,String> {
315 let Assignment { lattice_hash, elements, .. } = implicability;
316 let SafeElement { code: bottom, lattice_hash: ref_lattice_hash } = self.ref_bottom();
317 if lattice_hash == ref_lattice_hash { Ok( {
318 let zero = *zero_f64slx();
319 let neg_shift = match elements.get(&bottom) {
320 Some(w) => *w, None => zero,
321 };
322 let mapped = elements.iter()
323 .map(|(x,w)| (x.clone(), *w - neg_shift)).collect::<Vec<_>>();
324 for (_,w) in &mapped {
325 let w = (*w).unslx();
326 if !w.is_finite() || w.is_sign_negative() {
327 return Err(format!("mapped weight {w} is not finite or is sign negative"));
328 }
329 }
330 let elements = mapped.into_iter().filter(|(_,w)| (*w).unslx() > ASSIGNMENT_EPSILON).collect();
331 Assignment { elements, lattice_hash: *lattice_hash }
332 } ) } else { Err("Mismatching lattice hash".to_string()) }
333 }
334
335 fn implicability_from_credibility(&self, credibility: &Assignment<Self::Item>) -> Result<Assignment<Self::Item>,String> {
339 let Assignment { lattice_hash, elements, } = credibility;
340 let SafeElement { code: bottom, lattice_hash: ref_lattice_hash } = self.ref_bottom();
341 let SafeElement { code: top, .. } = self.ref_top();
342 let one = *one_f64slx();
343 let slx_assignment_epsilon: f64slx = ASSIGNMENT_EPSILON.slx();
344 if lattice_hash == ref_lattice_hash { Ok( {
345 let shift = match elements.get(&top) {
346 Some(w) => one - *w, None => one,
347 };
348 if shift <= - slx_assignment_epsilon { return Err("weight on top is greater than 1.0".to_string()) }
349 {
350 if let Some(w_bottom) = elements.get(&bottom) {
351 return Err(format!("weight on bottom is {w_bottom} not 0.0"))
352 }
353 }
354 let zero_weighted = unsafe { self.unsafe_bottom_to_top() }?
356 .filter(|e| !elements.contains_key(e))
357 .map(|e|(e,shift));
358 let weighted = zero_weighted.chain(
360 elements.clone().into_iter().map(|(x,w)| (x,w + shift))
361 ).collect::<Vec<_>>();
362 for (_,w) in &weighted { if !w.is_finite() || *w <= -slx_assignment_epsilon {
363 return Err(format!("mapped weight {w} is not finite or is sign negative"))
364 } }
365 let weighted_filtered = weighted.into_iter().filter(|(_,w)| w > &slx_assignment_epsilon).collect::<Vec<_>>();
366 let elements = weighted_filtered.clone().into_iter().collect::<HashMap<_,_>>();
368 Assignment { lattice_hash: *lattice_hash, elements, }
369 } ) } else { Err("Mismatching lattice hash".to_string()) }
370 }
371}
372
373pub trait ComplementedBeliefTransform: IterableLattice + ComplementedLattice where Self::Item: Eq + Ord + Hash + Clone, {
375 fn mass_from_plausibility(&self, plausibility: &Assignment<Self::Item>) -> Result<Assignment<Self::Item>,String> {
379 let Assignment { lattice_hash, elements, .. } = plausibility;
380 if lattice_hash == self.ref_lattice_hash() {
381 let mut mass = self.assignment();
382 let one = *one_f64slx();
383 for x in unsafe{ self.unsafe_top_to_bottom() }? {
384 let neg_x = unsafe { self.unsafe_not(&x) };
385 let mut wx = match elements.get(&x) {
386 Some(w) => one - *w, None => one,
387 }; for (y,wy) in &mass.elements.elements {
389 if unsafe{ self.unsafe_implied_join(&neg_x, y) } { wx -= *wy; }
390 }
391 if wx.abs() > ASSIGNMENT_EPSILON.slx() { unsafe { mass.unsafe_push(neg_x, wx) }?; }
392 }
393 mass.length_mid = (mass.elements.len() as u32).slx();
394 let slx2u32: u32slx = 2u32.slx();
395 mass.length_max = slx2u32 * mass.length_mid;
396 mass.normalize()?;
397 Ok(mass.into())
398 } else { Err("Mismatching lattice hash".to_string()) }
399 }
400}
401
402impl<L> BeliefTransform for L where L: IterableLattice, Self::Item: Ord + Hash, { }
403
404impl<L> ComplementedBeliefTransform for L
405 where L: IterableLattice + ComplementedLattice, Self::Item: Ord + Hash, { }
406
407
408pub mod experiment {
409 use crate::{
413 types::IntoSlx,
414 structs::Powerset,
415 traits::{ Lattice, BeliefTransform, ComplementedBeliefTransform, LatticeWithLeaves, }
416 };
417
418 pub fn exp_transform() -> Result<(),String> {
420 println!("======================= transform =====");
421 let lattice = Powerset::new(3,1024)
422 .expect("unexpected powwerset initialisation failure")
423 .set_iterators();
424 let (mut m1,mut m2) = (
425 lattice.assignment(),
426 lattice.assignment(),
427 );
428 let (prop_a, m_a,) = (lattice.leaf(0)?, 0.1.slx());
429 let (prop_b, m_b) = (lattice.leaf(1)?, 0.15.slx(),);
430 let (prop_c, m_c) = (lattice.leaf(2)?, 0.2.slx());
431 let (prop_bc, m_bc) = (lattice.join(&prop_b,&prop_c)?,0.2.slx());
432 let (prop_ca, m_ca) = (lattice.join(&prop_c,&prop_a)?,0.1.slx());
433 let (prop_ab, m_ab) = (lattice.join(&prop_a,&prop_b)?,0.25.slx());
434 let (prop_abc, m_abc) = (lattice.join(&prop_bc,&prop_a)?,0.0.slx());
435 m1.push(prop_a,m_a)?;
436 m1.push(prop_b,m_b)?;
437 m1.push(prop_c,m_c)?;
438 m1.push(prop_bc,m_bc)?;
439 m1.push(prop_ca,m_ca)?;
440 m1.push(prop_ab,m_ab)?;
441 m1.push(prop_abc,m_abc)?;
442 let m1 = m1.into();
443 let (prop_bot, m_bot,) = (lattice.meet(&prop_a,&prop_b)?, 0.05.slx());
445 let m_bc = 0.15.slx();
446 let m_ab = 0.15.slx();
447 let m_abc = 0.1.slx();
448 m2.push(prop_bot,m_bot)?;
449 m2.push(prop_a,m_a)?;
450 m2.push(prop_b,m_b)?;
451 m2.push(prop_c,m_c)?;
452 m2.push(prop_bc,m_bc)?;
453 m2.push(prop_ca,m_ca)?;
454 m2.push(prop_ab,m_ab)?;
455 m2.push(prop_abc,m_abc)?;
456 let m2 = m2.into();
457 for (m,nm) in [(m1,"m1"),(m2,"m2")] {
458 println!("===================== transform {nm}=====");
459 println!("------------------- implicability -----");
460 println!("m: {:?}", m);
461 let implicability = lattice.mass_to_implicability(&m)?;
462 println!("implicability: {:?}",implicability);
463 let back_m = lattice.mass_from_implicability(&implicability)?;
464 println!("back_m -> {:?}",back_m);
465 println!("--------------------- commonality -----");
466 println!("m: {:?}", m);
467 let commonality = lattice.mass_to_commonality(&m)?;
468 println!("commonality: {:?}",commonality);
469 let back_m = lattice.mass_from_commonality(&commonality)?;
470 println!("back_m -> {:?}",back_m);
471 println!("--------------------- credibility -----");
472 println!("m: {:?}", m);
473 let credibility = lattice.mass_to_credibility(&m)?;
474 println!("credibility: {:?}",credibility);
475 let back_m = lattice.mass_from_credibility(&credibility)?;
476 println!("back_m -> {:?}",back_m);
477 let implicability = lattice.implicability_from_credibility(&credibility)?;
478 println!("implicability: {:?}",implicability);
479 let back_credibility = lattice.implicability_to_credibility(&implicability)?;
480 println!("back_credibility: {:?}",back_credibility);
481 println!("-------------------- plausibility -----");
482 println!("m: {:?}", m);
483 let plausibility = lattice.mass_to_plausibility(&m)?;
484 println!("plausibility: {:?}",plausibility);
485 let back_m = lattice.mass_from_plausibility(&plausibility)?;
487 println!("back_m -> {:?}",back_m);
488 println!("----------------------- pignistic -----");
489 println!("m: {:?}", m);
490 let pignistic = lattice.mass_to_pignistic(&m)?;
491 println!("pignistic: {:?}", pignistic);
492 println!();
493 }
494 Ok(())
495 }
496}