Skip to main content

qualia_core_db/modalities/argumentation/
vaf.rs

1//! Value-based Argumentation Frameworks (Bench-Capon) — attacks succeed only when the attacker's
2//! value is not less preferred than the target's. This lets a **human-rights hierarchy** decide
3//! conflicts: an attack from a less-important value cannot defeat an argument grounded in a
4//! more-important (e.g. non-derogable) right. The result projects to a standard Dung framework of
5//! successful *defeats*, over which the usual semantics apply.
6
7use super::ArgumentationFramework;
8use std::collections::{HashMap, HashSet};
9
10/// A Dung framework augmented with a value per argument and an audience preference over values.
11#[derive(Debug, Clone)]
12pub struct ValueArgumentationFramework {
13    pub af: ArgumentationFramework,
14    /// argument id → its value (e.g. a human-rights principle hash).
15    pub value_of: HashMap<u64, u64>,
16    /// value → audience preference rank (higher = more important / more preferred).
17    pub value_rank: HashMap<u64, u32>,
18}
19
20impl ValueArgumentationFramework {
21    pub fn new(af: ArgumentationFramework) -> Self {
22        Self {
23            af,
24            value_of: HashMap::new(),
25            value_rank: HashMap::new(),
26        }
27    }
28
29    /// Assign argument `arg` the value `value`.
30    pub fn set_value(&mut self, arg: u64, value: u64) {
31        self.value_of.insert(arg, value);
32    }
33
34    /// Set the audience preference `rank` for `value` (higher = more preferred).
35    pub fn set_rank(&mut self, value: u64, rank: u32) {
36        self.value_rank.insert(value, rank);
37    }
38
39    fn rank_of_arg(&self, arg: u64) -> u32 {
40        self.value_of
41            .get(&arg)
42            .and_then(|v| self.value_rank.get(v))
43            .copied()
44            .unwrap_or(0)
45    }
46
47    /// An attack `attacker → target` **defeats** `target` iff
48    /// `rank(value(attacker)) >= rank(value(target))` — an attack from a less-preferred value
49    /// cannot defeat a more-preferred (e.g. non-derogable rights) argument.
50    pub fn defeats(&self, attacker: u64, target: u64) -> bool {
51        self.rank_of_arg(attacker) >= self.rank_of_arg(target)
52    }
53
54    /// Project to the Dung framework of successful **defeats** (attacks that pass the value test).
55    pub fn defeat_framework(&self) -> ArgumentationFramework {
56        let mut out = ArgumentationFramework::new();
57        for arg in self.af.arguments.values() {
58            out.add_argument(arg.clone());
59        }
60        for atk in &self.af.attacks {
61            if self.defeats(atk.attacker, atk.target) {
62                out.add_attack(atk.clone());
63            }
64        }
65        out
66    }
67
68    /// Grounded extension under the audience's value preference.
69    pub fn grounded_extension(&self) -> HashSet<u64> {
70        self.defeat_framework().grounded_extension()
71    }
72
73    /// Preferred extensions under the audience's value preference.
74    pub fn preferred_extensions(&self) -> Vec<HashSet<u64>> {
75        self.defeat_framework().preferred_extensions()
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::super::{Argument, Attack, AttackType};
82    use super::*;
83    use crate::NQuin;
84
85    fn arg(id: u64) -> Argument {
86        Argument::new(id, String::new(), Vec::new(), NQuin::default())
87    }
88    fn atk(a: u64, b: u64) -> Attack {
89        Attack {
90            attacker: a,
91            target: b,
92            attack_type: AttackType::Rebuttal,
93            strength: 1.0,
94        }
95    }
96
97    #[test]
98    fn higher_value_argument_survives_a_mutual_attack() {
99        // a ↔ b mutually attack. In a plain AF, neither is grounded.
100        let mut af = ArgumentationFramework::new();
101        af.add_argument(arg(1));
102        af.add_argument(arg(2));
103        af.add_attack(atk(1, 2));
104        af.add_attack(atk(2, 1));
105        assert!(
106            af.grounded_extension().is_empty(),
107            "plain AF: mutual attack → no acceptance"
108        );
109
110        // Now value(1)=rights (rank 10), value(2)=convenience (rank 1).
111        let mut vaf = ValueArgumentationFramework::new(af);
112        let rights = crate::q_hash("value:nonDerogableRight");
113        let convenience = crate::q_hash("value:convenience");
114        vaf.set_value(1, rights);
115        vaf.set_value(2, convenience);
116        vaf.set_rank(rights, 10);
117        vaf.set_rank(convenience, 1);
118
119        // 1 defeats 2 (10 >= 1); 2 does NOT defeat 1 (1 < 10). So only 1→2 survives → grounded {1}.
120        assert!(vaf.defeats(1, 2));
121        assert!(!vaf.defeats(2, 1));
122        let g = vaf.grounded_extension();
123        assert!(
124            g.contains(&1) && !g.contains(&2),
125            "the rights-grounded argument prevails"
126        );
127    }
128}