Skip to main content

argumentation_schemes/
aspic.rs

1//! ASPIC+ integration: feed a [`SchemeInstance`] into an
2//! [`argumentation::aspic::StructuredSystem`] as ordinary premises plus
3//! a defeasible rule.
4
5use crate::instance::SchemeInstance;
6use argumentation::aspic::{Literal, RuleId, StructuredSystem};
7
8/// Feed a scheme instance into a `StructuredSystem` as ordinary premises
9/// and a defeasible rule (premises → conclusion).
10///
11/// Returns the [`RuleId`] of the defeasible rule that was added, which
12/// can be used for preference ordering via
13/// [`StructuredSystem::prefer_rule`].
14///
15/// The instance's `premises` are added as ordinary (defeasible) premises
16/// via [`StructuredSystem::add_ordinary`]. The instance's `conclusion`
17/// (already polarised by the scheme's [`crate::scheme::ConclusionTemplate`])
18/// becomes the rule's conclusion.
19pub fn add_scheme_to_system(instance: &SchemeInstance, system: &mut StructuredSystem) -> RuleId {
20    for premise in &instance.premises {
21        system.add_ordinary(premise.clone());
22    }
23    system.add_defeasible_rule(instance.premises.clone(), instance.conclusion.clone())
24}
25
26/// Feed a critical question's counter-argument into a `StructuredSystem`
27/// as an ordinary premise asserting the counter-literal, plus a defeasible
28/// rule concluding the contrary of the original scheme's conclusion (rebut).
29///
30/// Returns the [`RuleId`] of the counter-rule.
31pub fn add_counter_argument(
32    counter_literal: &Literal,
33    target_conclusion: &Literal,
34    system: &mut StructuredSystem,
35) -> RuleId {
36    system.add_ordinary(counter_literal.clone());
37    let neg_conclusion = target_conclusion.contrary();
38    system.add_defeasible_rule(vec![counter_literal.clone()], neg_conclusion)
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use crate::critical::CriticalQuestion;
45    use crate::instance::instantiate;
46    use crate::scheme::*;
47    use crate::types::*;
48    use std::collections::HashMap;
49
50    fn expert_scheme() -> SchemeSpec {
51        SchemeSpec {
52            id: SchemeId(1),
53            name: "Argument from Expert Opinion".into(),
54            category: SchemeCategory::Epistemic,
55            premises: vec![
56                PremiseSlot::new("expert", "The expert", SlotRole::Agent),
57                PremiseSlot::new("domain", "Field", SlotRole::Domain),
58                PremiseSlot::new("claim", "The claim", SlotRole::Proposition),
59            ],
60            conclusion: ConclusionTemplate::positive("?claim is true", "?claim"),
61            critical_questions: vec![CriticalQuestion::new(
62                1,
63                "Is ?expert an expert?",
64                Challenge::SourceCredibility,
65            )],
66            metadata: SchemeMetadata {
67                citation: "Walton 2008".into(),
68                domain_tags: vec![],
69                presumptive: true,
70                strength: SchemeStrength::Moderate,
71            },
72        }
73    }
74
75    fn alice_bindings() -> HashMap<String, String> {
76        [
77            ("expert".to_string(), "alice".to_string()),
78            ("domain".to_string(), "military".to_string()),
79            ("claim".to_string(), "fortify_east".to_string()),
80        ]
81        .into_iter()
82        .collect()
83    }
84
85    #[test]
86    fn add_scheme_creates_argument_with_expected_conclusion() {
87        let scheme = expert_scheme();
88        let instance = instantiate(&scheme, &alice_bindings()).unwrap();
89
90        let mut system = StructuredSystem::new();
91        let _rule_id = add_scheme_to_system(&instance, &mut system);
92
93        assert!(!system.rules().is_empty());
94        let built = system.build_framework().unwrap();
95        let conclusion_args = built.arguments_with_conclusion(&Literal::atom("fortify_east"));
96        assert!(
97            !conclusion_args.is_empty(),
98            "expected at least one argument concluding fortify_east"
99        );
100    }
101
102    #[test]
103    fn counter_argument_filters_original_when_preferred() {
104        let scheme = expert_scheme();
105        let instance = instantiate(&scheme, &alice_bindings()).unwrap();
106
107        let mut system = StructuredSystem::new();
108        let main_rule = add_scheme_to_system(&instance, &mut system);
109
110        // Bob presses CQ1 by asserting the counter-literal.
111        let cq = &instance.critical_questions[0];
112        let counter_rule =
113            add_counter_argument(&cq.counter_literal, &instance.conclusion, &mut system);
114        // Prefer Bob's counter so it strictly defeats Alice's argument.
115        system.prefer_rule(counter_rule, main_rule).unwrap();
116
117        let built = system.build_framework().unwrap();
118        let preferred = built.framework.preferred_extensions().unwrap();
119        assert_eq!(preferred.len(), 1);
120
121        let has_fortify = built
122            .arguments_with_conclusion(&Literal::atom("fortify_east"))
123            .iter()
124            .any(|a| preferred[0].contains(&a.id));
125        assert!(
126            !has_fortify,
127            "original claim should be defeated when counter-rule is preferred"
128        );
129    }
130}