argumentation_values/
multi.rs1use crate::error::Error;
9use crate::framework::ValueBasedFramework;
10use crate::types::Audience;
11use std::collections::HashSet;
12use std::hash::Hash;
13
14pub struct MultiAudience<'a> {
16 audiences: &'a [Audience],
17}
18
19impl<'a> MultiAudience<'a> {
20 pub fn new(audiences: &'a [Audience]) -> Self {
24 Self { audiences }
25 }
26
27 pub fn audiences(&self) -> &[Audience] {
29 self.audiences
30 }
31
32 pub fn common_credulous<A>(
39 &self,
40 vaf: &ValueBasedFramework<A>,
41 ) -> Result<HashSet<A>, Error>
42 where
43 A: Clone + Eq + Hash + Ord + std::fmt::Debug,
44 {
45 if self.audiences.is_empty() {
46 return Ok(vaf.base().arguments().cloned().collect());
47 }
48
49 let per_audience: Vec<HashSet<A>> = self
51 .audiences
52 .iter()
53 .map(|aud| -> Result<HashSet<A>, Error> {
54 let defeat = vaf.defeat_graph(aud)?;
55 let extensions = defeat.preferred_extensions().map_err(Error::from)?;
56 let mut credulous = HashSet::new();
57 for ext in extensions {
58 for arg in ext {
59 credulous.insert(arg);
60 }
61 }
62 Ok(credulous)
63 })
64 .collect::<Result<_, _>>()?;
65
66 let mut iter = per_audience.into_iter();
68 let Some(mut acc) = iter.next() else {
69 return Ok(HashSet::new());
70 };
71 for next in iter {
72 acc.retain(|a| next.contains(a));
73 }
74 Ok(acc)
75 }
76
77 pub fn common_grounded<A>(
81 &self,
82 vaf: &ValueBasedFramework<A>,
83 ) -> Result<HashSet<A>, Error>
84 where
85 A: Clone + Eq + Hash + Ord + std::fmt::Debug,
86 {
87 if self.audiences.is_empty() {
88 return Ok(vaf.base().arguments().cloned().collect());
89 }
90
91 let per_audience: Vec<HashSet<A>> = self
92 .audiences
93 .iter()
94 .map(|aud| vaf.grounded_for(aud))
95 .collect::<Result<_, _>>()?;
96
97 let mut iter = per_audience.into_iter();
98 let Some(mut acc) = iter.next() else {
99 return Ok(HashSet::new());
100 };
101 for next in iter {
102 acc.retain(|a| next.contains(a));
103 }
104 Ok(acc)
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::types::{Value, ValueAssignment};
112 use argumentation::ArgumentationFramework;
113
114 fn hal_carla() -> ValueBasedFramework<&'static str> {
115 let mut base = ArgumentationFramework::new();
116 for arg in ["h1", "c1", "h2", "c2"] {
117 base.add_argument(arg);
118 }
119 base.add_attack(&"h1", &"c1").unwrap();
120 base.add_attack(&"c1", &"h1").unwrap();
121 base.add_attack(&"c2", &"h2").unwrap();
122 base.add_attack(&"h2", &"c1").unwrap();
123
124 let mut values = ValueAssignment::new();
125 values.promote("h1", Value::new("life"));
126 values.promote("c1", Value::new("property"));
127 values.promote("h2", Value::new("fairness"));
128 values.promote("c2", Value::new("life"));
129
130 ValueBasedFramework::new(base, values)
131 }
132
133 #[test]
134 fn common_grounded_across_opposing_audiences_yields_only_unanimous_winners() {
135 let vaf = hal_carla();
136 let life_first = Audience::total([Value::new("life"), Value::new("property")]);
137 let property_first = Audience::total([Value::new("property"), Value::new("life")]);
138 let auds = [life_first, property_first];
139 let multi = MultiAudience::new(&auds);
140 let common = multi.common_grounded(&vaf).unwrap();
141 assert!(common.contains("c2"));
143 assert!(!common.contains("h1"));
144 assert!(!common.contains("c1"));
145 }
146
147 #[test]
148 fn empty_audience_set_returns_all_arguments() {
149 let vaf = hal_carla();
150 let multi = MultiAudience::new(&[]);
151 let common = multi.common_grounded(&vaf).unwrap();
152 assert_eq!(common.len(), 4);
153 }
154
155 #[test]
156 fn common_credulous_is_superset_of_common_grounded() {
157 let vaf = hal_carla();
158 let life_first = Audience::total([Value::new("life"), Value::new("property")]);
159 let property_first = Audience::total([Value::new("property"), Value::new("life")]);
160 let auds = [life_first, property_first];
161 let multi = MultiAudience::new(&auds);
162 let credulous = multi.common_credulous(&vaf).unwrap();
163 let grounded = multi.common_grounded(&vaf).unwrap();
164 for arg in &grounded {
165 assert!(credulous.contains(arg), "common_grounded must subset common_credulous");
166 }
167 }
168}