argumentation/semantics/
stable.rs1use super::subset_enum::{sorted_args_or_too_large, subset_from_bits};
4use crate::framework::ArgumentationFramework;
5use std::collections::HashSet;
6use std::hash::Hash;
7
8impl<A: Clone + Eq + Hash + Ord> ArgumentationFramework<A> {
9 pub fn stable_extensions(&self) -> Result<Vec<HashSet<A>>, crate::Error> {
17 let args = sorted_args_or_too_large(self)?;
18 let n = args.len();
19 let mut results = Vec::new();
20 for bits in 0u64..(1u64 << n) {
21 let s = subset_from_bits(&args, bits);
22 if !self.is_conflict_free(&s) {
23 continue;
24 }
25 let attacks_all_outside = self
26 .arguments()
27 .filter(|a| !s.contains(*a))
28 .all(|a| self.attackers(a).iter().any(|att| s.contains(*att)));
29 if attacks_all_outside {
30 results.push(s);
31 }
32 }
33 Ok(results)
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40
41 #[test]
42 fn stable_of_figure_1_is_singleton_ac() {
43 let mut af = ArgumentationFramework::new();
44 af.add_argument("a");
45 af.add_argument("b");
46 af.add_argument("c");
47 af.add_attack(&"a", &"b").unwrap();
48 af.add_attack(&"b", &"c").unwrap();
49 let se = af.stable_extensions().unwrap();
50 assert_eq!(se.len(), 1);
51 let expected: HashSet<&str> = ["a", "c"].into_iter().collect();
52 assert!(se.contains(&expected));
53 }
54
55 #[test]
56 fn stable_of_odd_cycle_is_empty() {
57 let mut af = ArgumentationFramework::new();
58 af.add_argument("a");
59 af.add_argument("b");
60 af.add_argument("c");
61 af.add_attack(&"a", &"b").unwrap();
62 af.add_attack(&"b", &"c").unwrap();
63 af.add_attack(&"c", &"a").unwrap();
64 let se = af.stable_extensions().unwrap();
65 assert!(se.is_empty());
66 }
67
68 #[test]
69 fn stable_subset_of_preferred() {
70 let mut af = ArgumentationFramework::new();
71 af.add_argument("a");
72 af.add_argument("b");
73 af.add_attack(&"a", &"b").unwrap();
74 af.add_attack(&"b", &"a").unwrap();
75 let se = af.stable_extensions().unwrap();
76 let pe = af.preferred_extensions().unwrap();
77 for s in &se {
78 assert!(pe.contains(s));
79 }
80 }
81}