From 3fd16180be8f7bc32e4158796f26136c4d6e6285 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:49:37 +0200 Subject: [PATCH 01/43] refactoring --- .../ArgumentationBasedCausalReasoner.java | 24 ++ ...umentationBasedCounterfactualReasoner.java | 4 + .../dung/causal/reasoner/CausalReasoner.java | 17 + .../CausalStatement.java | 4 +- .../CounterfactualStatement.java | 10 +- .../InterventionalStatement.java | 9 +- .../causal/syntax/CausalKnowledgeBase.java | 137 ++++++--- .../arg/dung/causal/syntax/CausalModel.java | 36 +-- .../dung/causal/syntax/InducedArgument.java | 8 +- .../arg/dung/causal/syntax/InducedTheory.java | 6 +- .../arg/dung/causal/syntax/KnowledgeBase.java | 48 ++- .../causal/syntax/StructuralCausalModel.java | 290 ++++++++++++++++++ .../arg/dung/writer/TikzWriter.java | 272 ++++++++++++++++ .../syntax/CausalKnowledgeBaseTest.java | 6 +- .../dung/causal/syntax/CausalModelTest.java | 10 +- .../syntax/CounterfactualStatementTest.java | 3 +- .../dung/causal/syntax/InducedTheoryTest.java | 2 +- .../syntax/InterventionalStatementTest.java | 3 +- 18 files changed, 762 insertions(+), 127 deletions(-) create mode 100644 org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java create mode 100644 org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java create mode 100644 org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/CausalReasoner.java rename org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/{syntax => semantics}/CausalStatement.java (95%) rename org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/{syntax => semantics}/CounterfactualStatement.java (88%) rename org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/{syntax => semantics}/InterventionalStatement.java (91%) create mode 100644 org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/StructuralCausalModel.java create mode 100644 org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/writer/TikzWriter.java diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java new file mode 100644 index 000000000..44eabb6d6 --- /dev/null +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java @@ -0,0 +1,24 @@ +package org.tweetyproject.arg.dung.causal.reasoner; + +import org.tweetyproject.arg.dung.causal.semantics.CausalStatement; +import org.tweetyproject.logics.pl.syntax.PlFormula; + +import java.util.Collection; + +public class ArgumentationBasedCausalReasoner { + public boolean query(CausalStatement statement) { + return true; + } + + public boolean query(Collection observations, PlFormula effect) { + return true; + } + + public Collection getConclusions(PlFormula observation) { + return null; + } + + public Collection getConclusions(Collection observations) { + return null; + } +} diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java new file mode 100644 index 000000000..96815c10c --- /dev/null +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java @@ -0,0 +1,4 @@ +package org.tweetyproject.arg.dung.causal.reasoner; + +public class ArgumentationBasedCounterfactualReasoner { +} diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/CausalReasoner.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/CausalReasoner.java new file mode 100644 index 000000000..34bd0b5a3 --- /dev/null +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/CausalReasoner.java @@ -0,0 +1,17 @@ +package org.tweetyproject.arg.dung.causal.reasoner; + +import org.tweetyproject.logics.pl.syntax.PlFormula; + +import java.util.Collection; + +public abstract class CausalReasoner { + + protected Collection observations; + + /* + remove observations from CKB + CKB is a BeliefSet for the struct equations + a set of assumptions + observations only in the causal reasoner + causal reasoner has method to create AF, get conclusions, verify conclusions + */ +} diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalStatement.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java similarity index 95% rename from org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalStatement.java rename to org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java index e44f06ae1..e3f4a7469 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalStatement.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java @@ -16,10 +16,12 @@ * * Copyright 2023 The TweetyProject Team */ -package org.tweetyproject.arg.dung.causal.syntax; +package org.tweetyproject.arg.dung.causal.semantics; import java.util.HashSet; +import org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.arg.dung.causal.syntax.InducedTheory; import org.tweetyproject.arg.dung.util.DungTheoryPlotter; import org.tweetyproject.logics.pl.syntax.PlFormula; diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CounterfactualStatement.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java similarity index 88% rename from org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CounterfactualStatement.java rename to org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java index 6084dcf85..f5a2f4dfb 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CounterfactualStatement.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java @@ -16,11 +16,13 @@ * * Copyright 2023 The TweetyProject Team */ -package org.tweetyproject.arg.dung.causal.syntax; +package org.tweetyproject.arg.dung.causal.semantics; import java.util.HashMap; import java.util.HashSet; +import org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.arg.dung.causal.syntax.InducedTheory; import org.tweetyproject.arg.dung.util.DungTheoryPlotter; import org.tweetyproject.logics.pl.syntax.PlFormula; import org.tweetyproject.logics.pl.syntax.Proposition; @@ -29,9 +31,7 @@ * This class describes a counterfactual causal statement like: * given phi, if v had been x then rho would be true * - * Reference: "Argumentation-based Causal and Counterfactual Reasoning" by - * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, published at 1st International Workshop on Argumentation - * for eXplainable AI (ArgXAI, co-located with COMMA ’22), September 12, 2022 + * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm 'Argumentation-based Causal and Counterfactual Reasoning' 1st International Workshop on Argumentation for eXplainable AI, 2022" * * @author Julian Sander * @version TweetyProject 1.23 @@ -86,7 +86,7 @@ protected CausalKnowledgeBase getIntervenedTwinModel(CausalKnowledgeBase cKbase) } var newKnowledgeBase = new CausalKnowledgeBase(twin, cKbase.getAssumptions()); - newKnowledgeBase.addAll(cKbase.getBeliefsWithoutStructuralEquations()); + newKnowledgeBase.addAll(cKbase.getObservations()); return newKnowledgeBase; } } diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InterventionalStatement.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java similarity index 91% rename from org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InterventionalStatement.java rename to org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java index ed648fcda..bdd8ca03d 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InterventionalStatement.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java @@ -16,11 +16,14 @@ * * Copyright 2023 The TweetyProject Team */ -package org.tweetyproject.arg.dung.causal.syntax; +package org.tweetyproject.arg.dung.causal.semantics; import java.util.HashMap; import java.util.HashSet; +import org.tweetyproject.arg.dung.causal.semantics.CausalStatement; +import org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.arg.dung.causal.syntax.InducedTheory; import org.tweetyproject.arg.dung.util.DungTheoryPlotter; import org.tweetyproject.logics.pl.syntax.PlFormula; import org.tweetyproject.logics.pl.syntax.Proposition; @@ -37,7 +40,7 @@ * @version TweetyProject 1.23 * */ -public class InterventionalStatement extends CausalStatement{ +public class InterventionalStatement extends CausalStatement { private HashMap interventions; /** @@ -98,7 +101,7 @@ protected CausalKnowledgeBase getIntervenedCopy(CausalKnowledgeBase cKbase) { } var newKnowledgeBase = new CausalKnowledgeBase(causalModel, cKbase.getAssumptions()); - newKnowledgeBase.addAll(cKbase.getBeliefsWithoutStructuralEquations()); + newKnowledgeBase.addAll(cKbase.getObservations()); return newKnowledgeBase; } } diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java index 05b4e8c5e..e1ef1b12c 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java @@ -18,72 +18,98 @@ */ package org.tweetyproject.arg.dung.causal.syntax; +import java.util.Collection; import java.util.HashSet; import java.util.Set; +import org.tweetyproject.logics.pl.reasoner.SimplePlReasoner; import org.tweetyproject.logics.pl.syntax.Negation; +import org.tweetyproject.logics.pl.syntax.PlBeliefSet; import org.tweetyproject.logics.pl.syntax.PlFormula; +import org.tweetyproject.logics.pl.syntax.Proposition; /** * This class describes a causal knowledge base. * - * Reference: "Argumentation-based Causal and Counterfactual Reasoning" by - * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, published at 1st International Workshop on Argumentation - * for eXplainable AI (ArgXAI, co-located with COMMA ’22), September 12, 2022 - * - * @author Julian Sander - * @version TweetyProject 1.23 + * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, 'Argumentation-based Causal and Counterfactual Reasoning', 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), 2022" * + * @author Julian Sander, Lars Bengel */ -public class CausalKnowledgeBase extends KnowledgeBase { +public class CausalKnowledgeBase extends PlBeliefSet { /** - * A causal model, which is the basis of this knowledge base. + * Explicit storage of causal model of this causal knowledge base */ - private CausalModel causalModel; + protected StructuralCausalModel model; + protected Collection assumptions; + + public CausalKnowledgeBase(StructuralCausalModel model) { + this.model = model.clone(); + this.assumptions = new HashSet<>(); + } /** - * Creates a new causal knowledge base - * @param facts Causal model representing the causal origin of this knowledge base. + * Initializes a new causal knowledge base + * @param model some causal model * @param assumptions Set of assumptions about the background atoms of the causal model. */ - public CausalKnowledgeBase(CausalModel facts, Set assumptions) { - super(assumptions); - this.causalModel = facts; - for(var assumption : assumptions) { - if(!assumption.isLiteral()) { - throw new IllegalArgumentException("There is at least one background assumption, that is not a literal."); - } - - for(var atom : assumption.getAtoms()) { - if(!facts.getBackGroundAtoms().contains(atom)) { - throw new IllegalArgumentException("There is at least one assumption, that contains an atom which is not a background atom."); - } - } + public CausalKnowledgeBase(StructuralCausalModel model, Collection assumptions) { + this(model); + //this.formulas.addAll(model); + for(PlFormula assumption : assumptions) { + this.addAssumption(assumption); } - for(var u : facts.getBackGroundAtoms()) { + for(Proposition u : model.getBackgroundAtoms()) { if(!assumptions.contains(u) && !assumptions.contains(new Negation(u))) { - throw new IllegalArgumentException("There is at least one background atom without any assumption."); + throw new IllegalArgumentException("There must be at least one assumption for each background atom"); } } } - + /** - * @return The underlying causal model of this instance. + * Adds an assumption to this knowledge base. + * + * @param assumption The PlFormula representing the assumption to be added + * @return "True" iff the assumption was successfully added */ - public CausalModel getCausalModel() { - return this.causalModel; + public boolean addAssumption(PlFormula assumption) { + if (!assumption.isLiteral()) throw new IllegalArgumentException("Assumption must be literal"); + if (!model.getBackgroundAtoms().containsAll(assumption.getAtoms())) throw new IllegalArgumentException("Can only assume background atoms"); + return this.assumptions.add(assumption); } - + /** - * @return Returns all propositional formulas of this knowledge base, this includes the structural equations of the underlying causal model. + * Removes an assumption from this knowledge base. + * + * @param assumption The assumption to be removed. + * @return true if the assumption was successfully removed, false if it was not found in the set. */ - @Override - public HashSet getBeliefs(){ - var output = new HashSet(this.formulas); - output.addAll(this.causalModel.getStructuralEquations()); - return output; + public boolean removeAssumption(PlFormula assumption) { + return this.assumptions.remove(assumption); + } + + /** + * @return Set of {@link PlFormula}, which are the assumptions of this instance. + */ + public Collection getAssumptions() { + return new HashSet<>(this.assumptions); + } + + public StructuralCausalModel getCausalModel() { + return this.model.clone(); + } + + /** + * Returns all propositional formulas of this knowledge base, i.e., + * the structural equations of the corresponding causal model + * + * @return the set of structural equations + */ + public Collection getBeliefs(){ + Collection result = new HashSet<>(formulas); + result.addAll(model.getStructuralEquations()); + return result; } /** @@ -91,8 +117,22 @@ public HashSet getBeliefs(){ * * @return a set of beliefs without structural equations */ - public HashSet getBeliefsWithoutStructuralEquations(){ - return new HashSet(this.formulas); + public Collection getObservations(){ + return new HashSet<>(formulas); + } + + /** + * Determines whether the specified conclusion can be inferred from the given premises via this knowledge base. + * + * @param premises Set of formulas, which will be added to this knowledge base + * @param conclusion Formula, which is checked to be a conclusion of the combination of this instance and the specified premises or not + * @return "True" iff the specified formula is a conclusion of this knowledge base and the specified set of premises. + */ + public boolean entails(Collection premises, PlFormula conclusion) { + Collection beliefs = this.getBeliefs(); + beliefs.addAll(premises); + PlBeliefSet beliefSet = new PlBeliefSet(beliefs); + return new SimplePlReasoner().query(beliefSet, conclusion); } /** @@ -101,28 +141,25 @@ public HashSet getBeliefsWithoutStructuralEquations(){ * @param premises Set of formulas which are added to this knowledge base to get to the returned conlusions. * @return Set of formulas, that can be concluded from this knowledge base, if the specified formulas are added. */ - public HashSet getSingelAtomConclusions(Set premises){ - var conclusions = new HashSet(); - for(var formula : this.causalModel.getStructuralEquations()) { - for(var atom : formula.getAtoms()) { - if(this.entails(premises, atom)) { + public Collection getSingleAtomConclusions(Collection premises){ + Collection conclusions = new HashSet<>(); + for(PlFormula formula : this.model.getStructuralEquations()) { + for(Proposition atom : formula.getAtoms()) { + if (this.entails(premises, atom)) { conclusions.add(atom); - }else{ - var negAtom = new Negation(atom); + } else { + PlFormula negAtom = new Negation(atom); if(this.entails(premises, negAtom)){ conclusions.add(negAtom); } } } } - return conclusions; } @Override public CausalKnowledgeBase clone() { - var output = new CausalKnowledgeBase(this.causalModel, this.getAssumptions()); - output.addAll(this.formulas); - return output; + return new CausalKnowledgeBase(this.model, this.getAssumptions()); } } diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalModel.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalModel.java index 76ebd858e..9f878c50e 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalModel.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalModel.java @@ -33,13 +33,9 @@ /** * This class describes a causal model. * - * Reference: "Argumentation-based Causal and Counterfactual Reasoning" by - * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, published at 1st International Workshop on Argumentation - * for eXplainable AI (ArgXAI, co-located with COMMA ’22), September 12, 2022 + * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, 'Argumentation-based Causal and Counterfactual Reasoning', 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), 2022" * * @author Julian Sander - * @version TweetyProject 1.23 - * */ public class CausalModel extends PlBeliefSet { @@ -222,7 +218,7 @@ public boolean addBackgroundAtom(Proposition atom) { var newBackgroundAtoms = new HashSet<>(this.backGroundAtoms); newBackgroundAtoms.add(atom); - CausalModel.checkCorrectForm(newBackgroundAtoms, this.explainableAtoms, this.getStructuralEquations()); + //CausalModel.checkCorrectForm(newBackgroundAtoms, this.explainableAtoms, this.getStructuralEquations()); return this.backGroundAtoms.add(atom); } @@ -244,7 +240,7 @@ public boolean addExplainableAtom(Proposition atom, Equivalence structuralEquati var newEquations = this.getStructuralEquations(); newEquations.add(structuralEquation); - CausalModel.checkCorrectForm(this.backGroundAtoms, newExplainableAtoms, newEquations); + //CausalModel.checkCorrectForm(this.backGroundAtoms, newExplainableAtoms, newEquations); this.explainableAtoms.add(atom); this.add(structuralEquation); return true; @@ -280,7 +276,7 @@ public boolean addStructuralEquation(Equivalence equation) { } } - CausalModel.checkCorrectForm(newBackGround, newExplainable, newEquations); + //CausalModel.checkCorrectForm(newBackGround, newExplainable, newEquations); this.add(equation); this.explainableAtoms.addAll(this.explainableAtoms); this.backGroundAtoms.addAll(this.backGroundAtoms); @@ -335,14 +331,14 @@ public HashSet getExplainableAtoms() { * * @return A new {@link HashSet} containing all the structural equations. */ -public HashSet getStructuralEquations() { +public Collection getStructuralEquations() { var output = new HashSet(); for (var formula : this.formulas) { if (formula instanceof Equivalence) { output.add((Equivalence) formula); } } - return output; + return null; } @@ -372,8 +368,8 @@ public CausalModel getTwinModel() { for(int i = 0; i < occ; i++) { eqReplaced = (Equivalence) eq.replace(originalExpAtom, counterfactualCopy, i + 1); } - eqsToRemove.add(eq); - eqsToAdd.add(eqReplaced); + //eqsToRemove.add(eq); + //eqsToAdd.add(eqReplaced); } counterFactualEquations.removeAll(eqsToRemove); @@ -381,7 +377,7 @@ public CausalModel getTwinModel() { } structuralEquations.addAll(counterFactualEquations); - return new CausalModel(structuralEquations); + return null;//return new CausalModel(structuralEquations); } @Override @@ -406,12 +402,12 @@ public void intervene(Proposition v, boolean x) { } var eqToRemove = new HashSet(); - for(var eq : this.getStructuralEquations()) { - var leftHandSide = eq.getFormulas().getFirst(); - if(leftHandSide.getAtoms().contains(v)) { - eqToRemove.add(eq); - } - } + //for(var eq : this.getStructuralEquations()) { + // var leftHandSide = eq.getFormulas().getFirst(); + // if(leftHandSide.getAtoms().contains(v)) { + // eqToRemove.add(eq); + // } + //} if(eqToRemove.isEmpty()) { throw new NoSuchElementException("There is a explainable atom, without any structural equation"); } @@ -426,7 +422,7 @@ public void intervene(Proposition v, boolean x) { @Override public CausalModel clone() { - return new CausalModel(this.getStructuralEquations()); + return null;//new CausalModel(this.getStructuralEquations()); } private void commonConstructor(Set backGroundAtoms, Set explainableAtoms, Set structuralEquations) { diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedArgument.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedArgument.java index aed3198f7..ae3dbbef4 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedArgument.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedArgument.java @@ -25,17 +25,13 @@ import org.tweetyproject.logics.pl.syntax.PlFormula; /** - * This class is responsible for the representation of an {@link Argument} that was induced by a {@link CausalKnowledgeBase} - * - * Reference: "Argumentation-based Causal and Counterfactual Reasoning" by - * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, published at 1st International Workshop on Argumentation - * for eXplainable AI (ArgXAI, co-located with COMMA ’22), September 12, 2022 + * This class is responsible for the representation of an {@link Argument} that was induced from a {@link CausalKnowledgeBase} * * @author Julian Sander * @version TweetyProject 1.23 * */ -public class InducedArgument extends Argument{ +public class InducedArgument extends Argument { private static String generateName(Set premises, PlFormula conclusion) { return "(" + premises.toString() + "," + conclusion.toString() + ")"; diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheory.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheory.java index 7e2c5ed5e..23a543a6a 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheory.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheory.java @@ -31,9 +31,7 @@ /** * This class describes an {@link DungTheory abstract argumentation framework} that was induced by a {@link CausalKnowledgeBase} * - * Reference "Argumentation-based Causal and Counterfactual Reasoning" by - * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, published at 1st International Workshop on Argumentation - * for eXplainable AI (ArgXAI, co-located with COMMA ’22), September 12, 2022 + * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, 'Argumentation-based Causal and Counterfactual Reasoning', 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), 2022" * * @see DungTheory * @@ -52,7 +50,7 @@ public InducedTheory(CausalKnowledgeBase knowledgeBase) { this.knowledgeBase = knowledgeBase; for(var subSetAssumptions : new SetTools().subsets(knowledgeBase.getAssumptions())) { - for(var conclusion : knowledgeBase.getSingelAtomConclusions(subSetAssumptions)) { + for(var conclusion : knowledgeBase.getSingleAtomConclusions(subSetAssumptions)) { try { var argument = new InducedArgument(knowledgeBase, subSetAssumptions, conclusion); this.add(argument); diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/KnowledgeBase.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/KnowledgeBase.java index 99524ace5..f36a1d1e8 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/KnowledgeBase.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/KnowledgeBase.java @@ -18,6 +18,7 @@ */ package org.tweetyproject.arg.dung.causal.syntax; +import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -26,50 +27,45 @@ import org.tweetyproject.logics.pl.syntax.PlFormula; /** - * This class describes a knowledge base. - * - * Reference: "Argumentation-based Causal and Counterfactual Reasoning" by - * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, published at 1st International Workshop on Argumentation - * for eXplainable AI (ArgXAI, co-located with COMMA ’22), September 12, 2022 + * This class describes a knowledge base consisting of a set of formulas (knowledge) and a set of assumptions. * * @author Julian Sander - * @version TweetyProject 1.23 - * + * @author Lars Bengel */ -public class KnowledgeBase extends PlBeliefSet{ +public class KnowledgeBase extends PlBeliefSet { /** - * set of background assumptions
-
- * There is at least one background assumption for each background atom (in K). - * A background assumption for an atom u is a literal l \in {u, \neg{u}}. + * set of background assumptions */ - private HashSet assumptions; + private Collection assumptions; /** - * Constructs a knowledge base with a specified set of background assumptions. + * Constructs a knowledge base with a specified set of formulas and assumptions. * * @param assumptions A set of propositional logic formulas representing the background assumptions. */ - public KnowledgeBase(Set assumptions) { - super(); + public KnowledgeBase(Collection formulas, Collection assumptions) { + super(formulas); this.assumptions = new HashSet<>(assumptions); } /** - * Adds a background assumption to this knowledge base. + * Adds an assumption to this knowledge base. * - * @param assumption The PlFormula representing the assumption to be added. - * @return true if the assumption was successfully added, false if it already exists in the set. + * @param assumption The PlFormula representing the assumption to be added + * @return "True" iff the assumption was successfully added */ public boolean addAssumption(PlFormula assumption) { + if (!assumption.isLiteral()) throw new IllegalArgumentException("Assumption must be literal"); return this.assumptions.add(assumption); } /** - * Computes if a specified conclusion could be drawn from adding the specified premises to this instance. - * @param premises Set of formulas, which will be added to this knowledge base. - * @param conclusion Formula, which is checked to be a conclusion of the combination of this instance and the specified premises or not. - * @return TRUE iff the specified formula is a conclusion of this knowledge base and the specified set of premises. + * Determines whether the specified conclusion can be inferred from the given premises via this knowledge base. + * + * @param premises Set of formulas, which will be added to this knowledge base + * @param conclusion Formula, which is checked to be a conclusion of the combination of this instance and the specified premises or not + * @return "True" iff the specified formula is a conclusion of this knowledge base and the specified set of premises. */ public boolean entails(Set premises, PlFormula conclusion) { var beliefs = this.getBeliefs(); @@ -91,11 +87,11 @@ public HashSet getAssumptions() { * @return A set of PlFormulas representing the beliefs held in this knowledge base. */ public HashSet getBeliefs(){ - return new HashSet(this.formulas); + return new HashSet<>(this.formulas); } /** - * Removes a background assumption from this knowledge base. + * Removes an assumption from this knowledge base. * * @param assumption The assumption to be removed. * @return true if the assumption was successfully removed, false if it was not found in the set. @@ -106,8 +102,6 @@ public boolean removeAssumption(PlFormula assumption) { @Override public KnowledgeBase clone() { - var output = new KnowledgeBase(this.assumptions); - output.addAll(this.formulas); - return output; + return new KnowledgeBase(formulas, this.assumptions); } } diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/StructuralCausalModel.java new file mode 100644 index 000000000..b7107caaa --- /dev/null +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/StructuralCausalModel.java @@ -0,0 +1,290 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ + +package org.tweetyproject.arg.dung.causal.syntax; + +import org.tweetyproject.commons.BeliefBase; +import org.tweetyproject.commons.BeliefSet; +import org.tweetyproject.commons.Signature; +import org.tweetyproject.logics.pl.syntax.*; + +import java.util.*; + +/** + * This class describes a structural causal model in the sense of Pearl. + * + * @see "Judea Pearl, 'Causality: models, reasoning and inference', 2000" + * + * @author Julian Sander + * @author Lars Bengel + */ +public class StructuralCausalModel implements BeliefBase, Collection { + private Collection backgroundAtoms; + private Map explainableAtoms; + + /** + * Initializes an empty causal model + */ + public StructuralCausalModel() { + backgroundAtoms = new HashSet<>(); + explainableAtoms = new HashMap<>(); + } + + public StructuralCausalModel(Collection equations) throws CyclicDependencyException { + this(); + Map formulas = new HashMap<>(); + Collection explainable = new HashSet<>(); + Collection atoms = new HashSet<>(); + for (PlFormula formula : equations) { + if (formula instanceof Equivalence) { + Equivalence equation = (Equivalence) formula; + if (!equation.getFormulas().getFirst().isLiteral()) { + throw new IllegalArgumentException("Left side of Structural Equation must be literal!"); + } + explainable.addAll(equation.getFormulas().getFirst().getAtoms()); + atoms.addAll(equation.getAtoms()); + formulas.put(equation.getFormulas().getFirst(), equation.getFormulas().getSecond()); + } else { + throw new IllegalArgumentException("Structural Equations must be Equivalences"); + } + } + atoms.removeAll(explainable); + this.addBackgroundAtoms(atoms); + while (!formulas.isEmpty()) { + boolean changed = false; + for (PlFormula lit : formulas.keySet()) { + try { + changed |= this.addExplainableAtom(lit, formulas.get(lit)); + formulas.remove(lit); + } catch (CyclicDependencyException ignored) { + } + } + if (!changed) throw new CyclicDependencyException("The given set contains formulas with cyclic dependencies."); + } + } + + public Collection getBackgroundAtoms() { + return new HashSet<>(backgroundAtoms); + } + + public Collection getExplainableAtoms() { + return new HashSet<>(explainableAtoms.keySet()); + } + + public Collection getAtoms() { + Collection result = new HashSet<>(getBackgroundAtoms()); + result.addAll(getExplainableAtoms()); + return result; + } + + public Collection getStructuralEquations() { + Collection result = new HashSet<>(); + for (Proposition atom : explainableAtoms.keySet()) { + result.add(new Equivalence(atom, explainableAtoms.get(atom))); + } + return result; + } + + public PlFormula getCause(Proposition atom) { + return explainableAtoms.get(atom); + } + + public boolean addBackgroundAtom(Proposition atom) { + return backgroundAtoms.add(atom); + } + + public boolean addBackgroundAtoms(Collection atoms) { + return backgroundAtoms.addAll(atoms); + } + + public boolean addExplainableAtom(PlFormula atom, PlFormula cause) throws CyclicDependencyException { + if (!atom.isLiteral() || atom instanceof Negation) throw new IllegalArgumentException("Atom must be a proposition"); + Proposition prop = (Proposition) atom; + if (cause.getAtoms().contains(prop)) throw new IllegalArgumentException("Causal relation cannot be cyclic. 'cause' must not contain variable."); + + if (!getAtoms().containsAll(cause.getAtoms())) throw new CyclicDependencyException("Cause contains atoms that are not yet part of the causal model."); + + PlFormula ret = explainableAtoms.put(prop, cause); + return !cause.equals(ret); + } + + /** + * Constructs the twin model for this causal model, i.e., it creates for each structural equation a copy where all + * explainable atoms X are replaced by a twin version X* + * + * @return The twin model + */ + public StructuralCausalModel getTwinModel() { + Collection structuralEquations = this.getStructuralEquations(); + + StructuralCausalModel model = null; + // copy the model + try { + model = new StructuralCausalModel(structuralEquations); + } catch (CyclicDependencyException ignored) { + } + assert model != null; + + // create twin copy for each structural equation + Map twinEquations = new HashMap<>(); + for (PlFormula equation : structuralEquations) { + PlFormula atom = ((Equivalence) equation).getFormulas().getFirst(); + PlFormula cause = ((Equivalence) equation).getFormulas().getSecond(); + if (atom instanceof Proposition) { + Proposition twinAtom = new Proposition(((Proposition) atom).getName() + "*"); + PlFormula twinCause = cause.clone(); + for (Proposition prop : cause.getAtoms()) { + if (model.getBackgroundAtoms().contains(prop)) continue; + twinCause = twinCause.replace(prop, new Proposition(((Proposition) atom).getName() + "*"), cause.numberOfOccurrences(prop)); + } + twinEquations.put(twinAtom, twinCause); + } else { + throw new UnsupportedOperationException("Unsupported Structural Equation Type"); + } + } + + // add twin equations + while (!twinEquations.isEmpty()) { + for (PlFormula lit : twinEquations.keySet()) { + try { + this.addExplainableAtom(lit, twinEquations.get(lit)); + twinEquations.remove(lit); + } catch (CyclicDependencyException ignored) { + } + } + } + + return model; + } + + /** + * Performs an intervention on the explainable atom 'v' by setting it to a given truth value. + * Removes the original cause of the atom and thus permanently changes the causal model + * @param v some explainable atom + * @param x Truth value of the intervention + */ + public void intervene(Proposition v, boolean x) { + if(!this.explainableAtoms.containsKey(v)){ + throw new IllegalArgumentException("The specified variable has to be an explainable atom of the causal model"); + } + + if(x) { + this.explainableAtoms.put(v, new Tautology()); + }else { + this.explainableAtoms.put(v, new Contradiction()); + } + } + + @Override + public StructuralCausalModel clone() { + try { + return new StructuralCausalModel(this.getStructuralEquations()); + } catch (CyclicDependencyException ignored) { + // will never happen + } + return null; + } + + @Override + public Signature getMinimalSignature() { + PlSignature sig = new PlSignature(getBackgroundAtoms()); + sig.addAll(getExplainableAtoms()); + return sig; + + } + + @Override + public int size() { + return getExplainableAtoms().size(); + } + + @Override + public boolean isEmpty() { + return getExplainableAtoms().isEmpty(); + } + + @Override + public boolean contains(Object o) { + if (o instanceof Proposition) { + return getBackgroundAtoms().contains(o) || getExplainableAtoms().contains(o); + } else if (o instanceof PlFormula) { + return explainableAtoms.containsValue(o) || getStructuralEquations().contains(o); + } + return false; + } + + @Override + public Iterator iterator() { + return getStructuralEquations().iterator(); + } + + @Override + public Object[] toArray() { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public T[] toArray(T[] a) { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public boolean add(PlFormula plFormula) { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public boolean containsAll(Collection c) { + boolean result = true; + for (Object o : c) { + result &= contains(o); + } + return result; + } + + @Override + public boolean addAll(Collection c) { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public void clear() { + throw new UnsupportedOperationException("Not Implemented"); + } + + public static class CyclicDependencyException extends Throwable { + public CyclicDependencyException(String string) { + } + } +} diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/writer/TikzWriter.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/writer/TikzWriter.java new file mode 100644 index 000000000..b4a7be4db --- /dev/null +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/writer/TikzWriter.java @@ -0,0 +1,272 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ + +package org.tweetyproject.arg.dung.writer; + +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.Attack; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.commons.util.SetTools; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * Writer for exporting an argumentation framework into LaTeX-code via the argumentation package. + * + * @see "https://ctan.org/pkg/argumentation" + * + * @author Lars Bengel + */ +public class TikzWriter extends AbstractDungWriter { + + protected Argument[] intToArg; + protected Map argToInt; + + protected void initialize(DungTheory aaf) { + // Initialize mapping from Argument to ID and vice versa + intToArg = new Argument[aaf.size() + 1]; + argToInt = new HashMap<>(); + int v = 1; + for (Argument argument : aaf) { + intToArg[v] = argument; + argToInt.put(argument, v); + v++; + } + } + + protected void findPlanarMapping(DungTheory aaf, int width, int height) { + + } + + protected boolean isSufficientlyPlanar(DungTheory aaf, int width, int height) { + for (Argument argument : aaf) { + Collection neighboring = new HashSet<>(); + int arg_id = argToInt.get(argument); + int arg_row = (arg_id-1) % width; + int arg_col = (arg_id-1) / width; + + // check whether all neighbors in the graph are also neighbors in the grid + for (int x : Arrays.asList(-1,0,1)) { + for (int y : Arrays.asList(-1,0,1)) { + if (arg_row+x < 0 || arg_row+x >= width || arg_col+y < 0 || arg_col+y >= height) { + continue; + } + int new_arg = (arg_col+y)*width + arg_row+x + 1; + neighboring.add(intToArg[new_arg]); + } + } + + if (!neighboring.containsAll(aaf.getNeighbors(argument))) { + return false; + } + } + return true; + } + + @Override + public void write(DungTheory aaf, File f) throws IOException { + PrintWriter writer = new PrintWriter(f, StandardCharsets.UTF_8); + writer.write(this.write(aaf)); + writer.close(); + } + + /** + * Writes an argumentation framework into a LaTeX-code string. The AF is shaped in a square grid + * @param aaf an argumentation framework + * @return String with the LaTeX representation of the AF + */ + public String write(DungTheory aaf) { + // square grid + int width = (int) Math.ceil(Math.sqrt(aaf.size())); + int height = (int) Math.floor(Math.sqrt(aaf.size())); + return write(aaf, width, height); + } + + /** + * Writes an argumentation framework into a LaTeX-code string. The AF is shaped in a grid with the given dimensions + * @param aaf an argumentation framework + * @param width width of the AF-grid + * @param height height of the AF-grid + * @return String with the LaTeX representation of the AF + */ + public String write(DungTheory aaf, int width, int height) { + initialize(aaf); + + StringWriter out = new StringWriter(); + PrintWriter writer = new PrintWriter(out); + writer.println("\\begin{af}"); + writer.print(writeArguments(aaf, width, height)); + writer.print(writeSelfAttacks(aaf, width, height)); + writer.print(writeAttacks(aaf)); + writer.println("\\end{af}"); + writer.close(); + return out.toString(); + } + + protected String writeArguments(Collection aaf, int width, int height) { + StringBuilder s = new StringBuilder(); + // Write arguments + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int arg_id = i * width + j + 1; + if (arg_id > aaf.size()) { + continue; + } + if (arg_id == 1) { + s.append("\t").append(writeArgument(intToArg[arg_id], arg_id, "")); + continue; + } + String pos; + int rel_arg_id; + if (j == 0) { + if (i == 0) { + continue; + } + rel_arg_id = (i - 1) * width + j + 1; + pos = "below=of arg"; + } else { + rel_arg_id = i * width + j - 1 + 1; + pos = "right=of arg"; + } + pos = pos + rel_arg_id; + s.append("\t").append(writeArgument(intToArg[arg_id], arg_id, pos)); + } + } + s.append("\n"); + return s.toString(); + } + + /** + * Method that converts an argument to the corresponding tikz string to create a tikz-node + * @param arg some argument + * @param arg_id the id of the given argument + * @param pos the relative position of the argument + * @return string with tikz code + */ + protected String writeArgument(Argument arg, int arg_id, String pos) { + return String.format("\t\\argument[%s]{arg%s}{$%s$}%n", pos, arg_id, writeArgument(arg)); + } + + /** + * Writes a string representation in LaTeX-code for a given argument + * @param arg some argument + * @return LaTeX-compatible string representation of the argument + */ + protected String writeArgument(Argument arg) { + if (arg.getName().contains("_")) { + // argument name of the form 'a_1, a_2, ...' + String[] a = arg.getName().split("_"); + return String.format("%s_{%s}",a[0],a[1]); + } else if (arg.getName().substring(0,1).matches("[a-zA-Z]+") && arg.getName().substring(1).matches("[0-9]+")) { + // argument name of the form 'a1, a2, ...' + return String.format("%s_{%s}",arg.getName().charAt(0),arg.getName().substring(1)); + } else { + // other name formats + return arg.getName(); + } + } + + protected String writeAttacks(DungTheory aaf) { + StringBuilder s = new StringBuilder(); + // Write attacks + Collection attacksAdded = new HashSet<>(); + for (Attack att : aaf.getAttacks()) { + if (att.getAttacker().equals(att.getAttacked())) { + continue; + } else if (aaf.isAttackedBy(att.getAttacker(), att.getAttacked())) { + if (!attacksAdded.contains(new Attack(att.getAttacked(), att.getAttacker()))) { + s.append("\t").append(writeAttack(argToInt.get(att.getAttacker()), argToInt.get(att.getAttacked()), "dual")); + attacksAdded.add(att); + } + } else { + s.append("\t").append(writeAttack(argToInt.get(att.getAttacker()), argToInt.get(att.getAttacked()))); + attacksAdded.add(att); + } + } + s.append("\n"); + return s.toString(); + } + + protected String writeAttack(int argId1, int argId2) { + return writeAttack(argId1, argId2, "single"); + } + + protected String writeAttack(int argId1, int argId2, String mode) { + return switch (mode) { + case "single" -> String.format("\\attack{arg%s}{arg%s}%n", argId1, argId2); + case "dual" -> String.format("\\dualattack{arg%s}{arg%s}%n", argId1, argId2); + case "bend" -> String.format("\\attack[bend right]{arg%s}{arg%s}%n", argId1, argId2); + default -> throw new IllegalArgumentException("Unknown attack mode"); + }; + } + + protected String writeSelfAttacks(DungTheory aaf, int width, int height) { + StringBuilder s = new StringBuilder(); + // Write self-attacks + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int arg_id = i * width + j + 1; + if (arg_id > aaf.size()) { + continue; + } + Argument arg = intToArg[arg_id]; + if (!aaf.isAttackedBy(arg, arg)) { + continue; + } + if (i == 0) { + // argument in first row of grid -> print loop above + s.append(String.format( + "\t\\selfattack[out=60,in=120,looseness=6]{arg%s}", + arg_id) + ).append("\n"); + } else if (i == height - 1) { + // argument in last row of grid -> print below + s.append(String.format( + "\t\\selfattack[out=240,in=300,looseness=6]{arg%s}", + arg_id) + ).append("\n"); + } else if (j == width - 1) { + // argument on last column of grid -> print to the right + s.append(String.format( + "\t\\selfattack[out=330,in=30,looseness=6]{arg%s}", + arg_id) + ).append("\n"); + } else if (j == 0) { + // argument in first column of grid -> print to the left + s.append(String.format( + "\t\\selfattack[out=150,in=210,looseness=6]{arg%s}", + arg_id) + ).append("\n"); + } else { + // somewhere in the center -> print top-right (NOTE: likely to be overlapping with something) + s.append(String.format( + "\t\\selfattack{arg%s}", + arg_id) + ).append("\n"); + } + } + } + return s.toString(); + } +} diff --git a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBaseTest.java b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBaseTest.java index b0f8362c7..9c7caf7f8 100644 --- a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBaseTest.java +++ b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBaseTest.java @@ -38,7 +38,7 @@ class CausalKnowledgeBaseTest { /** - * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase#CausalKnowledgeBase(org.tweetyproject.arg.dung.causal.syntax.CausalModel, java.util.Set)}. + * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase#CausalKnowledgeBase(CausalModel, java.util.Set)}. */ @Test void testCausalKnowledgeBase() { @@ -96,7 +96,7 @@ void testEntails() { } /** - * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase#getSingelAtomConclusions(java.util.Set)}. + * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase#getSingleAtomConclusions(java.util.Set)}. */ @Test void testGetConclusions() { @@ -114,7 +114,7 @@ void testGetConclusions() { //Act var premises = new HashSet(); premises.add(new Conjunction(new Negation(corona), fever)); - Assertions.assertTrue(causalKnowledgeBase.getSingelAtomConclusions(premises).contains(influenza)); + Assertions.assertTrue(causalKnowledgeBase.getSingleAtomConclusions(premises).contains(influenza)); } private CausalModel setupModel(Proposition covid, Proposition corona, Proposition shortOfBreath, Proposition atRisk, Proposition influenza, Proposition fever) { diff --git a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalModelTest.java b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalModelTest.java index 46ee98920..4bd6d6d46 100644 --- a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalModelTest.java +++ b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalModelTest.java @@ -130,8 +130,8 @@ void testIntervene() { } /** - * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.CausalModel#CausalModel(java.util.Set, java.util.Set, java.util.Set)}. - * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.CausalModel#CausalModel(java.util.Set)}. + * Test method for {@link CausalModel#CausalModel(java.util.Set, java.util.Set, java.util.Set)}. + * Test method for {@link CausalModel#CausalModel(java.util.Set)}. */ @Test void testCausalModel() { @@ -172,9 +172,9 @@ void testCausalModel() { // Assertions.assertEquals(model1, model2); } - private CausalModel BuildFirstOption(Proposition corona, Proposition influenza, Proposition atRisk, Proposition covid, - Proposition flu, Proposition shortOfBreath, Proposition fever, Proposition chills, - Equivalence eq1, Equivalence eq2, Equivalence eq3, Equivalence eq4, Equivalence eq5) { + private CausalModel BuildFirstOption(Proposition corona, Proposition influenza, Proposition atRisk, Proposition covid, + Proposition flu, Proposition shortOfBreath, Proposition fever, Proposition chills, + Equivalence eq1, Equivalence eq2, Equivalence eq3, Equivalence eq4, Equivalence eq5) { var u = new HashSet(); u.add(corona); diff --git a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CounterfactualStatementTest.java b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CounterfactualStatementTest.java index 3f40f1962..1333fa646 100644 --- a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CounterfactualStatementTest.java +++ b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CounterfactualStatementTest.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.tweetyproject.arg.dung.causal.semantics.CounterfactualStatement; import org.tweetyproject.logics.pl.syntax.Conjunction; import org.tweetyproject.logics.pl.syntax.Disjunction; import org.tweetyproject.logics.pl.syntax.Equivalence; @@ -37,7 +38,7 @@ class CounterfactualStatementTest { /** - * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.CounterfactualStatement#holds(org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase)}. + * Test method for {@link CounterfactualStatement#holds(org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase)}. */ @Test void testHolds() { diff --git a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheoryTest.java b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheoryTest.java index 3dccbda6c..6cb892fb3 100644 --- a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheoryTest.java +++ b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheoryTest.java @@ -145,7 +145,7 @@ void testCausalTheory() { var premises2 = new HashSet(); var formula = new Conjunction(new Negation(corona), fever); premises2.add(formula); - Assertions.assertTrue(causalKnowledgeBase2.getSingelAtomConclusions(premises2).contains(influenza)); + Assertions.assertTrue(causalKnowledgeBase2.getSingleAtomConclusions(premises2).contains(influenza)); //Act causalKnowledgeBase2.add(formula); diff --git a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InterventionalStatementTest.java b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InterventionalStatementTest.java index 84019466d..d79758752 100644 --- a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InterventionalStatementTest.java +++ b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InterventionalStatementTest.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.tweetyproject.arg.dung.causal.semantics.InterventionalStatement; import org.tweetyproject.logics.pl.syntax.Conjunction; import org.tweetyproject.logics.pl.syntax.Disjunction; import org.tweetyproject.logics.pl.syntax.Equivalence; @@ -37,7 +38,7 @@ class InterventionalStatementTest { /** - * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.InterventionalStatement#holds(org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase)}. + * Test method for {@link InterventionalStatement#holds(org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase)}. */ @Test void testHolds() { From 8fe0619f70c7987c9d746f5d98baa4b5c47a95ad Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:10:30 +0200 Subject: [PATCH 02/43] refactored representation of causal arguments and theories --- .../causal/semantics/CausalStatement.java | 2 - .../semantics/CounterfactualStatement.java | 4 +- .../semantics/InterventionalStatement.java | 6 +- .../causal/syntax/CausalKnowledgeBase.java | 37 +- .../arg/dung/causal/syntax/CausalModel.java | 434 ------------------ .../dung/causal/syntax/InducedArgument.java | 75 +-- .../arg/dung/causal/syntax/InducedTheory.java | 83 ++-- .../arg/dung/causal/syntax/KnowledgeBase.java | 107 ----- .../causal/syntax/StructuralCausalModel.java | 66 ++- 9 files changed, 130 insertions(+), 684 deletions(-) delete mode 100644 org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalModel.java delete mode 100644 org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/KnowledgeBase.java diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java index e3f4a7469..94c310698 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java @@ -29,8 +29,6 @@ * This class describes a causal statement, such as an interventional or counterfactual statement. * * @author Julian Sander - * @version TweetyProject 1.23 - * */ public abstract class CausalStatement { private HashSet conclusions; diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java index f5a2f4dfb..73d5504dd 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java @@ -32,10 +32,8 @@ * given phi, if v had been x then rho would be true * * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm 'Argumentation-based Causal and Counterfactual Reasoning' 1st International Workshop on Argumentation for eXplainable AI, 2022" - * - * @author Julian Sander - * @version TweetyProject 1.23 * + * @author Julian Sander */ public class CounterfactualStatement extends InterventionalStatement { diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java index bdd8ca03d..0a8108763 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java @@ -32,13 +32,9 @@ * This class describes an interventional causal statement like: * given phi, if v would be x then rho would be true * - * Reference: "Argumentation-based Causal and Counterfactual Reasoning" by - * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, published at 1st International Workshop on Argumentation - * for eXplainable AI (ArgXAI, co-located with COMMA ’22), September 12, 2022 + * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm 'Argumentation-based Causal and Counterfactual Reasoning' 1st International Workshop on Argumentation for eXplainable AI, 2022" * * @author Julian Sander - * @version TweetyProject 1.23 - * */ public class InterventionalStatement extends CausalStatement { diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java index e1ef1b12c..a841884c1 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java @@ -14,33 +14,32 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * - * Copyright 2023 The TweetyProject Team + * Copyright 2024 The TweetyProject Team */ package org.tweetyproject.arg.dung.causal.syntax; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - import org.tweetyproject.logics.pl.reasoner.SimplePlReasoner; import org.tweetyproject.logics.pl.syntax.Negation; import org.tweetyproject.logics.pl.syntax.PlBeliefSet; import org.tweetyproject.logics.pl.syntax.PlFormula; import org.tweetyproject.logics.pl.syntax.Proposition; +import java.util.Collection; +import java.util.HashSet; + /** - * This class describes a causal knowledge base. + * This class describes a causal knowledge base which consists of a {@link StructuralCausalModel} and a set of background assumptions. * * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, 'Argumentation-based Causal and Counterfactual Reasoning', 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), 2022" * - * @author Julian Sander, Lars Bengel + * @author Julian Sander + * @author Lars Bengel */ public class CausalKnowledgeBase extends PlBeliefSet { - /** - * Explicit storage of causal model of this causal knowledge base - */ + /** Explicit storage of causal model of this causal knowledge base */ protected StructuralCausalModel model; + /** The set of background assumptions */ protected Collection assumptions; public CausalKnowledgeBase(StructuralCausalModel model) { @@ -49,13 +48,12 @@ public CausalKnowledgeBase(StructuralCausalModel model) { } /** - * Initializes a new causal knowledge base + * Initialize a new causal knowledge base * @param model some causal model * @param assumptions Set of assumptions about the background atoms of the causal model. */ public CausalKnowledgeBase(StructuralCausalModel model, Collection assumptions) { this(model); - //this.formulas.addAll(model); for(PlFormula assumption : assumptions) { this.addAssumption(assumption); } @@ -106,20 +104,11 @@ public StructuralCausalModel getCausalModel() { * * @return the set of structural equations */ - public Collection getBeliefs(){ + public Collection getBeliefs() { Collection result = new HashSet<>(formulas); result.addAll(model.getStructuralEquations()); return result; } - - /** - * Retrieves all propositional formulas of this knowledge base excluding the structural equations. - * - * @return a set of beliefs without structural equations - */ - public Collection getObservations(){ - return new HashSet<>(formulas); - } /** * Determines whether the specified conclusion can be inferred from the given premises via this knowledge base. @@ -138,10 +127,10 @@ public boolean entails(Collection premises, PlFormula conclusion) { /** * Returns all 1-atom-conclusions of this instance if the specified set of formulas is * used as premises. - * @param premises Set of formulas which are added to this knowledge base to get to the returned conlusions. + * @param premises Set of formulas which are added to this knowledge base to get to the returned conclusions. * @return Set of formulas, that can be concluded from this knowledge base, if the specified formulas are added. */ - public Collection getSingleAtomConclusions(Collection premises){ + public Collection getSingleAtomConclusions(Collection premises) { Collection conclusions = new HashSet<>(); for(PlFormula formula : this.model.getStructuralEquations()) { for(Proposition atom : formula.getAtoms()) { diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalModel.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalModel.java deleted file mode 100644 index 9f878c50e..000000000 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalModel.java +++ /dev/null @@ -1,434 +0,0 @@ -/* - * This file is part of "TweetyProject", a collection of Java libraries for - * logical aspects of artificial intelligence and knowledge representation. - * - * TweetyProject is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * Copyright 2023 The TweetyProject Team - */ -package org.tweetyproject.arg.dung.causal.syntax; - -import java.util.Collection; -import java.util.HashSet; -import java.util.NoSuchElementException; -import java.util.Set; - -import org.tweetyproject.logics.pl.syntax.Equivalence; -import org.tweetyproject.logics.pl.syntax.Negation; -import org.tweetyproject.logics.pl.syntax.PlBeliefSet; -import org.tweetyproject.logics.pl.syntax.PlFormula; -import org.tweetyproject.logics.pl.syntax.Proposition; -import org.tweetyproject.logics.pl.syntax.Tautology; - -/** - * This class describes a causal model. - * - * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, 'Argumentation-based Causal and Counterfactual Reasoning', 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), 2022" - * - * @author Julian Sander - */ -public class CausalModel extends PlBeliefSet { - -/** - * Constructs a causal model from a specified set of equivalences used as structural equations. - * This method populates two sets: one for explainable atoms and another for background atoms based on the provided structural equations. - * Explainable atoms are derived from the left-hand side of the equations if they consist solely of propositions. - * Atoms not explained by the left-hand side and appearing on the right-hand side of any equation are considered background atoms. - * - * @param structuralEquations A set of equivalences representing the structural equations of the causal model. Each equivalence must have a proposition on its left side. - * @param out_ExplainableAtoms An initially empty set that will be populated with propositions derived from the left-hand side of the structural equations. These atoms are considered explainable within the model. - * @param out_BackGroundAtoms An initially empty set that will be populated with atoms found in the right-hand side of the equations but not in the explainable atoms set, indicating these atoms are background conditions or factors. - * @throws IllegalArgumentException if any left-hand side of the equations does not consist of a proposition, indicating an invalid structural equation format. - */ - private static void buildModel(Set structuralEquations, HashSet out_ExplainableAtoms, - HashSet out_BackGroundAtoms) { - for(var eq : structuralEquations) { - var pair = eq.getFormulas(); - if(pair.getFirst() instanceof Proposition) { - out_ExplainableAtoms.addAll(pair.getFirst().getAtoms()); - } - else { - throw new IllegalArgumentException("only literal are acceptable at the left hand side of the equation"); - } - } - - for(var eq : structuralEquations) { - var pair = eq.getFormulas(); - for(var atom : pair.getSecond().getAtoms()) { - if(!out_ExplainableAtoms.contains(atom)) { - out_BackGroundAtoms.add(atom); - } - } - } - } - - /** - * Checks if the specified parameter comply to the necessary form of a causal model - * @param backGroundAtoms set of background atoms - * @param explainableAtoms set of explainable atoms - * @param structuralEquations boolean structural equations; one equation for each explainable atom, - * which is the only literal on exactly one side of the equation - * @return status of form - */ - private static boolean checkCorrectForm(Set backGroundAtoms, Set explainableAtoms, Set structuralEquations) { - for(var atom : explainableAtoms) { - boolean hasEQ = false; - for(var eq : structuralEquations) { - var pair = eq.getFormulas(); - if((pair.getFirst() instanceof Proposition) && pair.getFirst().getAtoms().contains(atom)) { - if(hasEQ) { - throw new IllegalArgumentException("has more than one boolean structural equation for an explainable atom"); - } - if(pair.getSecond().getAtoms().contains(atom)) { - throw new IllegalArgumentException("boolean structural equation has same explainable atom on both sides"); - } - hasEQ = true; - } - } - - if(!hasEQ) { - throw new IllegalArgumentException("has no boolean structural equation for an explainable atom"); - } - } - - for(var eq : structuralEquations) { - var pair = eq.getFormulas(); - CausalModel.checkIfOnlyExplainableBackgroundAtoms(backGroundAtoms, explainableAtoms, pair.getFirst()); - CausalModel.checkIfOnlyExplainableBackgroundAtoms(backGroundAtoms, explainableAtoms, pair.getSecond()); - } - - return true; - } - - /** - * Checks that the specified structural equations only use background or explainable atoms - * @param backGroundAtoms A set of propositions identified as background atoms. These are not directly explainable by the model but are necessary for defining the conditions under which other atoms are explained. - * @param explainableAtoms A set of propositions identified as explainable atoms. These atoms are those that the model can directly explain through its structural equations. - * @param formula The formula being checked. This formula should ideally be a structural equation that needs validation to ensure it uses only known atoms. - * @throws IllegalArgumentException if the formula uses atoms that are neither explainable nor background atoms, which violates the model's constraints. - */ - private static void checkIfOnlyExplainableBackgroundAtoms(Set backGroundAtoms, - Set explainableAtoms, PlFormula formula) { - for( var atom : formula.getAtoms()) { - if(!backGroundAtoms.contains(atom) && !explainableAtoms.contains(atom)) { - throw new IllegalArgumentException("boolean structural equation uses " - + "atoms different from the background or explainable atoms"); - } - } - } - - /** - * background atoms
-
- * Background atoms represent variables that are determined outside of the model. They are typically unobservable and uncontrollable. - */ - protected HashSet backGroundAtoms; - - /** - * explainable atoms
-
- * Every explainable atom v is functionally dependent on other atoms of the model. - */ - protected HashSet explainableAtoms; - - /** - * Creates an empty causal model - */ - public CausalModel() { - super(); - this.backGroundAtoms = new HashSet<>(); - this.explainableAtoms = new HashSet<>(); - } - - /** - * Creates a causal model - * @param structuralEquations Set of boolean structural equations; One equation for each explainable atom, - * which only occurs on the left side of the equation - */ - public CausalModel(Set structuralEquations) { - super(structuralEquations); - var explainableAtoms = new HashSet(); - var backGroundAtoms = new HashSet(); - - CausalModel.buildModel(structuralEquations, explainableAtoms, backGroundAtoms); - this.commonConstructor(backGroundAtoms, explainableAtoms, structuralEquations); - } - - /** - * Creates a causal model - * @param backGroundAtoms Set of background atoms - * @param explainableAtoms Set of explainable atoms - * @param structuralEquations Set of boolean structural equations; One equation for each explainable atom, - * which only occurs on the left side of the equation - */ - public CausalModel(Set backGroundAtoms, Set explainableAtoms, Set structuralEquations) { - this.commonConstructor(backGroundAtoms, explainableAtoms, structuralEquations); - } - - @Override - public boolean add(PlFormula formula) { - if(formula instanceof Equivalence) { - return super.add(formula); - } - - throw new IllegalArgumentException("only Equivalence formulas are eligible"); - } - - @Override - public boolean add(PlFormula... formulas) { - for(var formula : formulas) { - if(!(formula instanceof Equivalence)) { - throw new IllegalArgumentException("only Equivalence formulas are eligible"); - } - } - - return super.add(formulas); - } - - @Override - public boolean addAll(Collection formulas) { - for(var formula : formulas) { - if(!(formula instanceof Equivalence)) { - throw new IllegalArgumentException("only Equivalence formulas are eligible"); - } - } - - return super.addAll(formulas); - } - - /** - * Adds a specified atom to the background atoms of this instance. - * @param atom Atom to add to the background atoms of this instance. - * @return TRUE iff the atom was successfully added as a background atom to this instance. - * FALSE if the atom was already contained and leaves the set unchanged. - * @throws IllegalArgumentException if adding the specified atom would violate the definition of a causal model - */ - public boolean addBackgroundAtom(Proposition atom) { - if(this.backGroundAtoms.contains(atom)) { - return false; - } - var newBackgroundAtoms = new HashSet<>(this.backGroundAtoms); - newBackgroundAtoms.add(atom); - - //CausalModel.checkCorrectForm(newBackgroundAtoms, this.explainableAtoms, this.getStructuralEquations()); - return this.backGroundAtoms.add(atom); - } - - /** - * Adds a specified atom to the explainable atoms of this instance and the specified equivalence as a structural equation for this atom. - * @param atom Atom to add to the explainable atoms of this instance. - * @param structuralEquation Equation, having only the specified atom on the left side and - * a formula consisting only of atoms different than the specified atom on the right hand side of this equivalence - * @return TRUE iff the atom and the formula were successfully added to this instance. - * FALSE if the atom or the equation were already contained and leaves at least one of the sets unchanged. - * @throws IllegalArgumentException if adding the specified atom and or equation would violate the definition of a causal model - */ - public boolean addExplainableAtom(Proposition atom, Equivalence structuralEquation) { - if(this.explainableAtoms.contains(atom)) { - return false; - } - var newExplainableAtoms = new HashSet<>(this.explainableAtoms); - newExplainableAtoms.add(atom); - var newEquations = this.getStructuralEquations(); - newEquations.add(structuralEquation); - - //CausalModel.checkCorrectForm(this.backGroundAtoms, newExplainableAtoms, newEquations); - this.explainableAtoms.add(atom); - this.add(structuralEquation); - return true; - } - - /** - * Adds a specified equivalence to the structural equations of this instance. To this instance unknown explainable or background atoms - * are created and added in this process. - * @param equation Equation, having only one explainable atom on the left side and - * a formula on the right hand side of this equivalence consisting only of atoms different than the atom on the left - * @return TRUE iff the formula and all therein contained atoms were successfully added to this instance. - * FALSE if the equation was already contained and leaves the set unchanged. - * @throws IllegalArgumentException if adding the specified equation would violate the definition of a causal model - */ - public boolean addStructuralEquation(Equivalence equation) { - if(this.formulas.contains(equation)) { - return false; - } - var newEquations = this.getStructuralEquations(); - newEquations.add(equation); - var newExplainable = new HashSet<>(this.explainableAtoms); - var newBackGround = new HashSet<>(this.backGroundAtoms); - if(equation.getFormulas().getFirst() instanceof Proposition) { - newExplainable.addAll(equation.getFormulas().getFirst().getAtoms()); - } - else { - throw new IllegalArgumentException("only literal are acceptable at the left hand side of the equation"); - } - - for(var atom : equation.getFormulas().getSecond().getAtoms()) { - if(!newExplainable.contains(atom)) { - newBackGround.add(atom); - } - } - - //CausalModel.checkCorrectForm(newBackGround, newExplainable, newEquations); - this.add(equation); - this.explainableAtoms.addAll(this.explainableAtoms); - this.backGroundAtoms.addAll(this.backGroundAtoms); - return true; - } - - @Override - public boolean equals(Object o) { - if(o instanceof CausalModel) { - return false; - } - - var other = (CausalModel) o; - - if( other.backGroundAtoms.equals(this.backGroundAtoms) - && other.explainableAtoms.equals(this.explainableAtoms) - && other.formulas.equals(this.formulas)) { - return true; - } - else { - return super.equals(o); - } - } - -/** - * Retrieves a copy of the set of background atoms currently defined in the causal model. - * Background atoms are those elements that are used in the model's equations but are not directly explainable. - * Returning a copy of the set ensures that the internal state of the causal model cannot be altered directly. - * - * @return A new {@link HashSet} containing all the background atoms. - */ -public HashSet getBackGroundAtoms() { - return new HashSet<>(this.backGroundAtoms); -} - -/** - * Retrieves a copy of the set of explainable atoms currently defined in the causal model. - * Explainable atoms are those elements that the model explains directly through its structural equations. - * Returning a copy of the set helps maintain encapsulation and prevents external modifications to the model's state. - * - * @return A new {@link HashSet} containing all the explainable atoms. - */ -public HashSet getExplainableAtoms() { - return new HashSet<>(this.explainableAtoms); -} - -/** - * Retrieves a copy of the set of structural equations currently defined in the causal model. - * Structural equations are representations of equivalences that define how certain propositions (explainable atoms) - * depend on other atoms (background or explainable). Each equation must be an instance of {@link Equivalence}. - * This method filters the model's formulas to return only those that are instances of {@link Equivalence}. - * - * @return A new {@link HashSet} containing all the structural equations. - */ -public Collection getStructuralEquations() { - var output = new HashSet(); - for (var formula : this.formulas) { - if (formula instanceof Equivalence) { - output.add((Equivalence) formula); - } - } - return null; -} - - - /** - * @return A twin model to this instance. - */ - public CausalModel getTwinModel() { - var explainableAtoms = new HashSet<>(this.getExplainableAtoms()); - var structuralEquations = this.getStructuralEquations(); - var counterFactualEquations = new HashSet<>(structuralEquations); - - for(var originalExpAtom : this.explainableAtoms) { - var counterfactualCopy = new Proposition(originalExpAtom.getName() + "*"); - explainableAtoms.add(counterfactualCopy); - - var eqsToAdd = new HashSet(); - var eqsToRemove = new HashSet(); - - for(var eq : counterFactualEquations) { - if(!eq.getAtoms().contains(originalExpAtom)) { - continue; - } - - // replace all occurrences of the original atom with the counterfactual copy - var eqReplaced = eq; - int occ = eq.numberOfOccurrences(originalExpAtom); - for(int i = 0; i < occ; i++) { - eqReplaced = (Equivalence) eq.replace(originalExpAtom, counterfactualCopy, i + 1); - } - //eqsToRemove.add(eq); - //eqsToAdd.add(eqReplaced); - } - - counterFactualEquations.removeAll(eqsToRemove); - counterFactualEquations.addAll(eqsToAdd); - } - - structuralEquations.addAll(counterFactualEquations); - return null;//return new CausalModel(structuralEquations); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = (prime * result) + ((this.backGroundAtoms == null) ? 0 : this.backGroundAtoms.hashCode()) - + ((this.explainableAtoms == null) ? 0 : this.explainableAtoms.hashCode()) - + ((this.formulas == null) ? 0 : this.formulas.hashCode()); - return result; - } - - /** - * This method implements the interventional statement, by replacing all structural equations for the specified explainable atom "v" of this causal model with a - * formula "v = x". - * @param v Explainable atom of this causal model, which is to be intervened on. - * @param x Truth value of the intervention. - */ - public void intervene(Proposition v, boolean x) { - if(!this.explainableAtoms.contains(v)){ - throw new IllegalArgumentException("The specified variable has to be an explainable atom of the causal model"); - } - - var eqToRemove = new HashSet(); - //for(var eq : this.getStructuralEquations()) { - // var leftHandSide = eq.getFormulas().getFirst(); - // if(leftHandSide.getAtoms().contains(v)) { - // eqToRemove.add(eq); - // } - //} - if(eqToRemove.isEmpty()) { - throw new NoSuchElementException("There is a explainable atom, without any structural equation"); - } - - this.formulas.removeAll(eqToRemove); - if(x) { - this.formulas.add(new Equivalence(v, new Tautology())); - }else { - this.formulas.add(new Equivalence(v, new Negation(new Tautology()))); - } - } - - @Override - public CausalModel clone() { - return null;//new CausalModel(this.getStructuralEquations()); - } - - private void commonConstructor(Set backGroundAtoms, Set explainableAtoms, Set structuralEquations) { - CausalModel.checkCorrectForm(backGroundAtoms, explainableAtoms, structuralEquations); - this.backGroundAtoms = new HashSet<>(backGroundAtoms); - this.explainableAtoms = new HashSet<>(explainableAtoms); - this.addAll(structuralEquations); - } -} diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedArgument.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedArgument.java index ae3dbbef4..d3ce4f7ae 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedArgument.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedArgument.java @@ -14,10 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * - * Copyright 2023 The TweetyProject Team + * Copyright 2024 The TweetyProject Team */ package org.tweetyproject.arg.dung.causal.syntax; +import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -25,41 +26,32 @@ import org.tweetyproject.logics.pl.syntax.PlFormula; /** - * This class is responsible for the representation of an {@link Argument} that was induced from a {@link CausalKnowledgeBase} + * This class represents a logical {@link Argument} induced from a {@link CausalKnowledgeBase} + * An Argument consists of a set of assumptions (premises) and a conclusion * * @author Julian Sander - * @version TweetyProject 1.23 - * + * @author Lars Bengel */ public class InducedArgument extends Argument { - - private static String generateName(Set premises, PlFormula conclusion) { - return "(" + premises.toString() + "," + conclusion.toString() + ")"; - } - - private CausalKnowledgeBase knowledgeBase; - - private HashSet premises; - - private PlFormula conclusion; + /** Premises of the argument */ + private final Collection premises; + /** Conclusion of the argument */ + private final PlFormula conclusion; /** - * Creates a causal argument - * @param knowledgeBase The causal knowledge base based on which this argument is created. - * @param premises Set of formulas which have to be added to the knowledge base to be able to come to the the specified conclusion. - * @param conclusion Formula that concludes from the knowledge base, if the specified premises are added to the base. + * Initialize a new argument + * @param premises the set of premises of the argument + * @param conclusion the conclusion of the argument */ - public InducedArgument(CausalKnowledgeBase knowledgeBase, Set premises, PlFormula conclusion) { - super(InducedArgument.generateName(premises, conclusion)); - this.checkCorrectForm(knowledgeBase, premises, conclusion); + public InducedArgument(Collection premises, PlFormula conclusion) { + super(String.format("(%s, %s)", premises.toString(), conclusion.toString())); - this.knowledgeBase = knowledgeBase; this.premises = new HashSet<>(premises); this.conclusion = conclusion; } /** - * Returns the conclusion of this induced argument. + * Returns the conclusion of this argument. * * @return The conclusion that can be drawn from the knowledge base given the premises. */ @@ -68,44 +60,11 @@ public PlFormula getConclusion() { } /** - * Returns the causal knowledge base associated with this argument. - * - * @return The causal knowledge base from which this argument is derived. - */ - public CausalKnowledgeBase getKnowledgeBase() { - return this.knowledgeBase; - } - - /** - * Returns the premises of this induced argument. + * Returns the premises of this argument. * * @return A set containing all the premises required for deriving the conclusion in this argument. */ - public HashSet getPremises() { + public Collection getPremises() { return new HashSet<>(this.premises); } - - @Override - public String toString() { - return InducedArgument.generateName(this.premises, this.conclusion); - } - - private void checkCorrectForm(CausalKnowledgeBase knowledgeBase, Set premises, PlFormula conclusion) { - for(var formula : premises) { - if(!knowledgeBase.getAssumptions().contains(formula)) { - throw new IllegalArgumentException("premises is not a subset of the assumptions"); - } - - var lessPremises = new HashSet<>(premises); - lessPremises.remove(formula); - if(knowledgeBase.entails( lessPremises, conclusion)) { - throw new IllegalArgumentException("premises is not the minimal set of assumptions"); - } - } - - if(!knowledgeBase.entails( premises, conclusion)) { - throw new IllegalArgumentException("conclusion can not be infered from this knowledge base using this premises"); - } - } - } diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheory.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheory.java index 23a543a6a..a11e7a9c8 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheory.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheory.java @@ -14,14 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * - * Copyright 2023 The TweetyProject Team + * Copyright 2024 The TweetyProject Team */ package org.tweetyproject.arg.dung.causal.syntax; +import java.util.Collection; import java.util.HashSet; import java.util.Set; import org.tweetyproject.arg.dung.reasoner.AbstractExtensionReasoner; +import org.tweetyproject.arg.dung.semantics.Extension; import org.tweetyproject.arg.dung.semantics.Semantics; import org.tweetyproject.arg.dung.syntax.Argument; import org.tweetyproject.arg.dung.syntax.DungTheory; @@ -29,42 +31,37 @@ import org.tweetyproject.logics.pl.syntax.PlFormula; /** - * This class describes an {@link DungTheory abstract argumentation framework} that was induced by a {@link CausalKnowledgeBase} + * This class describes an {@link DungTheory argumentation framework} that was induced by a {@link CausalKnowledgeBase} * * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, 'Argumentation-based Causal and Counterfactual Reasoning', 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), 2022" * * @see DungTheory * * @author Julian Sander - * @version TweetyProject 1.23 + * @author Lars Bengel */ public class InducedTheory extends DungTheory { private CausalKnowledgeBase knowledgeBase; /** - * Creates an abstract argumentation framework, which was induced from a specified causal knowledge base - * @param knowledgeBase The causal knowledge base, which was the origin for this framework. + * Induces an argumentation framework from a causal knowledge base + * @param knowledgeBase some causal knowledge base */ public InducedTheory(CausalKnowledgeBase knowledgeBase) { this.knowledgeBase = knowledgeBase; - for(var subSetAssumptions : new SetTools().subsets(knowledgeBase.getAssumptions())) { - for(var conclusion : knowledgeBase.getSingleAtomConclusions(subSetAssumptions)) { - try { - var argument = new InducedArgument(knowledgeBase, subSetAssumptions, conclusion); - this.add(argument); - }catch(IllegalArgumentException e) { - // not possible - continue; - } + // TODO fix argument construction (minimality, relevance?) + for(Set subSetAssumptions : new SetTools().subsets(knowledgeBase.getAssumptions())) { + for(PlFormula conclusion : knowledgeBase.getSingleAtomConclusions(subSetAssumptions)) { + this.add(new InducedArgument(subSetAssumptions, conclusion)); } } - for(var arg1 : this.getNodes()) { - var cArg1 = (InducedArgument) arg1; - for(var arg2 : this.getNodes()) { - var cArg2 = (InducedArgument) arg2; + for(Argument arg1 : this.getNodes()) { + InducedArgument cArg1 = (InducedArgument) arg1; + for(Argument arg2 : this.getNodes()) { + InducedArgument cArg2 = (InducedArgument) arg2; if(this.checkUndercut(cArg1, cArg2)) { this.addAttack(cArg1, cArg2); } @@ -75,21 +72,22 @@ public InducedTheory(CausalKnowledgeBase knowledgeBase) { @Override public boolean add(Argument argument) { if(argument instanceof InducedArgument) { - if(((InducedArgument) argument).getKnowledgeBase().equals(this.knowledgeBase)) { - return super.add(argument); - } - else { - throw new IllegalArgumentException("argument is not defined on the same KnowledgeBase as the framework"); - } - }else { - throw new IllegalArgumentException("argument is not of type CausalArgument"); + if (!this.knowledgeBase.getAssumptions().containsAll(((InducedArgument) argument).getPremises())) { + throw new IllegalArgumentException("Argument contains invalid assumptions"); + } else if (!this.knowledgeBase.entails(((InducedArgument) argument).getPremises(), ((InducedArgument) argument).getConclusion())) { + throw new IllegalArgumentException("Argument has invalid conclusion"); + } // TODO check minimality + + return super.add(argument); + } else { + throw new IllegalArgumentException("Argument has wrong type"); } } @Override public boolean addAttack(Argument attacker, Argument attacked) { if(!((attacker instanceof InducedArgument) && (attacked instanceof InducedArgument))) { - throw new IllegalArgumentException("argument is not of type CausalArgument"); + throw new IllegalArgumentException("Argument has wrong type"); } return this.addAttack((InducedArgument) attacker, (InducedArgument) attacked); @@ -97,33 +95,34 @@ public boolean addAttack(Argument attacker, Argument attacked) { /** * Adds an attack from the first argument to the second to this theory. + * * @param attacker Argument which undercuts the second argument. * @param attacked Argument which is undercut by the first argument. - * @return TRUE iff the set of attacks was changed. - * FALSE if the attack was already element of the set. + * @return TRUE iff the set of attacks was changed */ public boolean addAttack(InducedArgument attacker, InducedArgument attacked) { if(!this.checkUndercut(attacker, attacked)) { - throw new IllegalArgumentException("the attacking argument does not undercut the attacked argument"); + throw new IllegalArgumentException("The attacking argument does not undercut the attacked argument"); } return super.addAttack(attacker, attacked); } /** - * This method checks if a specified fomula can be concluded from this instance, - * by checking if all stable extension contain at least one argument with the conclusion to check. - * @param conclusion Formula, which is in question to be a conclusion of this instance. + * This method checks if a specified formula can be concluded from this instance, + * by checking whether all stable extensions contain at least one argument with the given conclusion. + * + * @param conclusion some formula, which is in question to be a conclusion of this instance. * @return TRUE iff the conclusion can be drawn from this instance. FALSE if not. */ public boolean entails(PlFormula conclusion) { - var reasoner = AbstractExtensionReasoner.getSimpleReasonerForSemantics(Semantics.ST); - var extensions = reasoner.getModels(this); + AbstractExtensionReasoner reasoner = AbstractExtensionReasoner.getSimpleReasonerForSemantics(Semantics.ST); + Collection> extensions = reasoner.getModels(this); boolean allExtContainConclusion = true; - for(var ext : extensions) { + for(Extension ext : extensions) { boolean hasConclusion = false; - for(var argument : ext) { + for(Argument argument : ext) { if(((InducedArgument) argument).getConclusion().equals(conclusion)) { hasConclusion = true; break; @@ -142,7 +141,7 @@ public boolean entails(PlFormula conclusion) { * * @return A set of all InducedArguments within this theory. */ - public Set getArguments(){ + public Collection getArguments(){ var output = new HashSet(); for(var argument : this.getNodes()) { output.add((InducedArgument) argument); @@ -162,12 +161,12 @@ public CausalKnowledgeBase getKnowledgeBase() { /** * Checks if one argument undercuts another based on the conclusions and premises. * - * @param attacker The argument that potentially undercuts. - * @param victim The argument that is potentially undercut. - * @return true if the attacker undercuts the victim, otherwise false. + * @param attacker The argument that potentially undercuts. + * @param victim The argument that is potentially undercut. + * @return true iff the attacker undercuts the victim, otherwise false. */ private boolean checkUndercut(InducedArgument attacker, InducedArgument victim) { - for(var premise : victim.getPremises()) { + for(PlFormula premise : victim.getPremises()) { if(attacker.getConclusion().complement().equals(premise)) { return true; } diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/KnowledgeBase.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/KnowledgeBase.java deleted file mode 100644 index f36a1d1e8..000000000 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/KnowledgeBase.java +++ /dev/null @@ -1,107 +0,0 @@ -/* -* This file is part of "TweetyProject", a collection of Java libraries for -* logical aspects of artificial intelligence and knowledge representation. -* -* TweetyProject is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License version 3 as -* published by the Free Software Foundation. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -* -* Copyright 2023 The TweetyProject Team -*/ -package org.tweetyproject.arg.dung.causal.syntax; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import org.tweetyproject.logics.pl.reasoner.SimplePlReasoner; -import org.tweetyproject.logics.pl.syntax.PlBeliefSet; -import org.tweetyproject.logics.pl.syntax.PlFormula; - -/** - * This class describes a knowledge base consisting of a set of formulas (knowledge) and a set of assumptions. - * - * @author Julian Sander - * @author Lars Bengel - */ -public class KnowledgeBase extends PlBeliefSet { - - /** - * set of background assumptions - */ - private Collection assumptions; - - /** - * Constructs a knowledge base with a specified set of formulas and assumptions. - * - * @param assumptions A set of propositional logic formulas representing the background assumptions. - */ - public KnowledgeBase(Collection formulas, Collection assumptions) { - super(formulas); - this.assumptions = new HashSet<>(assumptions); - } - - /** - * Adds an assumption to this knowledge base. - * - * @param assumption The PlFormula representing the assumption to be added - * @return "True" iff the assumption was successfully added - */ - public boolean addAssumption(PlFormula assumption) { - if (!assumption.isLiteral()) throw new IllegalArgumentException("Assumption must be literal"); - return this.assumptions.add(assumption); - } - - /** - * Determines whether the specified conclusion can be inferred from the given premises via this knowledge base. - * - * @param premises Set of formulas, which will be added to this knowledge base - * @param conclusion Formula, which is checked to be a conclusion of the combination of this instance and the specified premises or not - * @return "True" iff the specified formula is a conclusion of this knowledge base and the specified set of premises. - */ - public boolean entails(Set premises, PlFormula conclusion) { - var beliefs = this.getBeliefs(); - beliefs.addAll(premises); - var beliefSet = new PlBeliefSet(beliefs); - return new SimplePlReasoner().query(beliefSet, conclusion); - } - - /** - * @return Set of {@link PlFormula}, which are the assumptions of this instance. - */ - public HashSet getAssumptions() { - return new HashSet<>(this.assumptions); - } - - /** - * Retrieves all beliefs (propositional formulas) stored in this knowledge base. - * - * @return A set of PlFormulas representing the beliefs held in this knowledge base. - */ - public HashSet getBeliefs(){ - return new HashSet<>(this.formulas); - } - - /** - * Removes an assumption from this knowledge base. - * - * @param assumption The assumption to be removed. - * @return true if the assumption was successfully removed, false if it was not found in the set. - */ - public boolean removeAssumption(PlFormula assumption) { - return this.assumptions.remove(assumption); - } - - @Override - public KnowledgeBase clone() { - return new KnowledgeBase(formulas, this.assumptions); - } -} diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/StructuralCausalModel.java index b7107caaa..a92fb198c 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/StructuralCausalModel.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/StructuralCausalModel.java @@ -16,11 +16,9 @@ * * Copyright 2024 The TweetyProject Team */ - package org.tweetyproject.arg.dung.causal.syntax; import org.tweetyproject.commons.BeliefBase; -import org.tweetyproject.commons.BeliefSet; import org.tweetyproject.commons.Signature; import org.tweetyproject.logics.pl.syntax.*; @@ -46,6 +44,15 @@ public StructuralCausalModel() { explainableAtoms = new HashMap<>(); } + /** + * Initializes a causal model based on the given structural equations. + * Every structural equation must be a logical equivalence of the form: {@code e <=> f(...)} + * where 'e' is a literal and f(...) is some logical formula representing the 'cause' of 'e'. + * The equations must be non-cyclic. + * + * @param equations the set of structural equations + * @throws CyclicDependencyException iff the equations are cyclic + */ public StructuralCausalModel(Collection equations) throws CyclicDependencyException { this(); Map formulas = new HashMap<>(); @@ -79,20 +86,36 @@ public StructuralCausalModel(Collection equations) throws CyclicDepen } } + /** + * Returns the set of background atoms of the causal model + * @return the set of background atoms + */ public Collection getBackgroundAtoms() { return new HashSet<>(backgroundAtoms); } + /** + * Returns the set of explainable atoms of the causal model + * @return the set of background atoms + */ public Collection getExplainableAtoms() { return new HashSet<>(explainableAtoms.keySet()); } + /** + * Returns the set of all atoms of the causal model + * @return the set of all atoms + */ public Collection getAtoms() { Collection result = new HashSet<>(getBackgroundAtoms()); result.addAll(getExplainableAtoms()); return result; } + /** + * Returns the structural equations of the causal model + * @return the structural equations + */ public Collection getStructuralEquations() { Collection result = new HashSet<>(); for (Proposition atom : explainableAtoms.keySet()) { @@ -101,22 +124,45 @@ public Collection getStructuralEquations() { return result; } + /** + * Get the cause of the given atom + * @param atom some atom of the causal model + * @return the cause of the atom + */ public PlFormula getCause(Proposition atom) { return explainableAtoms.get(atom); } + /** + * Adds a background atom to the model + * @param atom some new background atom + * @return true iff added successfully + */ public boolean addBackgroundAtom(Proposition atom) { return backgroundAtoms.add(atom); } + /** + * Add new background atoms to the model + * @param atoms a set of new background atoms + * @return true iff added successfully + */ public boolean addBackgroundAtoms(Collection atoms) { return backgroundAtoms.addAll(atoms); } + /** + * Adds a new explainable atom together with its cause to the causal model + * @param atom some new explainable atom + * @param cause the cause of the new atom + * @return true iff added successfully + * @throws CyclicDependencyException iff the new equation introduces a cyclic dependency + */ public boolean addExplainableAtom(PlFormula atom, PlFormula cause) throws CyclicDependencyException { if (!atom.isLiteral() || atom instanceof Negation) throw new IllegalArgumentException("Atom must be a proposition"); Proposition prop = (Proposition) atom; - if (cause.getAtoms().contains(prop)) throw new IllegalArgumentException("Causal relation cannot be cyclic. 'cause' must not contain variable."); + if (explainableAtoms.containsKey(prop)) throw new IllegalArgumentException("Atom already exists in the model"); + if (cause.getAtoms().contains(prop)) throw new CyclicDependencyException("Causal relation cannot be cyclic. 'cause' must not contain variable."); if (!getAtoms().containsAll(cause.getAtoms())) throw new CyclicDependencyException("Cause contains atoms that are not yet part of the causal model."); @@ -128,7 +174,7 @@ public boolean addExplainableAtom(PlFormula atom, PlFormula cause) throws Cyclic * Constructs the twin model for this causal model, i.e., it creates for each structural equation a copy where all * explainable atoms X are replaced by a twin version X* * - * @return The twin model + * @return the twin model */ public StructuralCausalModel getTwinModel() { Collection structuralEquations = this.getStructuralEquations(); @@ -179,16 +225,18 @@ public StructuralCausalModel getTwinModel() { * @param v some explainable atom * @param x Truth value of the intervention */ - public void intervene(Proposition v, boolean x) { + public StructuralCausalModel intervene(Proposition v, boolean x) { if(!this.explainableAtoms.containsKey(v)){ throw new IllegalArgumentException("The specified variable has to be an explainable atom of the causal model"); } - if(x) { - this.explainableAtoms.put(v, new Tautology()); - }else { - this.explainableAtoms.put(v, new Contradiction()); + StructuralCausalModel newModel = this.clone(); + if (x) { + newModel.explainableAtoms.put(v, new Tautology()); + } else { + newModel.explainableAtoms.put(v, new Contradiction()); } + return newModel; } @Override From a298832b2721c588fa80f71c3061a625ecd8528a Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:43:37 +0200 Subject: [PATCH 03/43] minor fix --- .../deductive/util/RandomDeductiveKnowledgeBaseGenerator.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/org-tweetyproject-arg-deductive/src/main/java/org/tweetyproject/arg/deductive/util/RandomDeductiveKnowledgeBaseGenerator.java b/org-tweetyproject-arg-deductive/src/main/java/org/tweetyproject/arg/deductive/util/RandomDeductiveKnowledgeBaseGenerator.java index f0c410c4c..9b8ab85aa 100644 --- a/org-tweetyproject-arg-deductive/src/main/java/org/tweetyproject/arg/deductive/util/RandomDeductiveKnowledgeBaseGenerator.java +++ b/org-tweetyproject-arg-deductive/src/main/java/org/tweetyproject/arg/deductive/util/RandomDeductiveKnowledgeBaseGenerator.java @@ -4,7 +4,6 @@ import org.tweetyproject.commons.BeliefSetIterator; import org.tweetyproject.logics.pl.syntax.*; -import javax.validation.constraints.NotNull; import java.util.*; /** @@ -88,7 +87,7 @@ public DeductiveKnowledgeBase next() { return kb; } - private static C randomChoice(@NotNull Collection c) throws IndexOutOfBoundsException { + private static C randomChoice(Collection c) throws IndexOutOfBoundsException { try { int num = random.nextInt(c.size()); for(C e: c) if (--num < 0) return e; From 5986255649d7367a2b967a19631022ec730ff922 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:43:52 +0200 Subject: [PATCH 04/43] adjusted causal statements --- .../ArgumentationBasedCausalReasoner.java | 7 ++ .../causal/semantics/CausalStatement.java | 63 +++++++--------- .../semantics/CounterfactualStatement.java | 62 +++++++--------- .../semantics/InterventionalStatement.java | 71 ++++++++----------- .../causal/syntax/CausalKnowledgeBase.java | 12 ++++ 5 files changed, 101 insertions(+), 114 deletions(-) diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java index 44eabb6d6..773f9816f 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java @@ -1,11 +1,18 @@ package org.tweetyproject.arg.dung.causal.reasoner; import org.tweetyproject.arg.dung.causal.semantics.CausalStatement; +import org.tweetyproject.arg.dung.causal.syntax.InducedTheory; import org.tweetyproject.logics.pl.syntax.PlFormula; import java.util.Collection; public class ArgumentationBasedCausalReasoner { + + public InducedTheory getInducedTheory(Collection observations) { + // TODO incorporate observations into argument construction + // Utilise DeductiveArgumentation package ?? + return null; + } public boolean query(CausalStatement statement) { return true; } diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java index 94c310698..3610906c0 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java @@ -14,64 +14,66 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * -* Copyright 2023 The TweetyProject Team +* Copyright 2024 The TweetyProject Team */ package org.tweetyproject.arg.dung.causal.semantics; -import java.util.HashSet; - import org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.arg.dung.causal.syntax.InducedTheory; import org.tweetyproject.arg.dung.util.DungTheoryPlotter; import org.tweetyproject.logics.pl.syntax.PlFormula; +import java.util.Collection; +import java.util.HashSet; + /** - * This class describes a causal statement, such as an interventional or counterfactual statement. - * + * This class describes a basic causal statement wrt. some {@link CausalKnowledgeBase} of the form:
+ * "Given phi, it follows that psi holds" + * * @author Julian Sander + * @author Lars Bengel */ -public abstract class CausalStatement { - private HashSet conclusions; +public class CausalStatement { + private Collection conclusions; - private HashSet premises; + private Collection observations; /** - * Creates a new causal statement. - * @param conclusions Conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. - * @param premises PlFormulas which have to be true, so that the conclusions can be drawn. + * Initializes a new causal statement. + * + * @param conclusions conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. + * @param premises observations of the causal atoms */ - public CausalStatement(HashSet conclusions, HashSet premises) { + public CausalStatement(Collection conclusions, Collection premises) { super(); this.conclusions = conclusions; - this.premises = premises; + this.observations = premises; } /** * Retrieves the conclusions of this causal statement. - * * @return A new HashSet containing all the conclusions of this causal statement. */ - public HashSet getConclusions(){ - return new HashSet(this.conclusions); + public Collection getConclusions(){ + return new HashSet<>(this.conclusions); } /** - * Retrieves the premises of this causal statement. - * + * Retrieves the observations of this causal statement. * @return A new HashSet containing all the premises required for the conclusions to hold. */ - public HashSet getPremises(){ - return new HashSet(this.premises); + public Collection getObservations(){ + return new HashSet<>(this.observations); } /** * Checks if this instance holds in the specified knowledge base. - * @param cKbase Causal knowledge base + * @param ckbase Causal knowledge base * @return TRUE iff this instance holds in the specified knowledge base. */ - public boolean holds(CausalKnowledgeBase cKbase) { + public boolean holds(CausalKnowledgeBase ckbase) { for(var conclusion : this.getConclusions()) { - if(!checkStatement(cKbase, conclusion)) { + if(!ckbase.entails(this.getObservations(), conclusion)) { return false; } } @@ -89,19 +91,8 @@ public boolean holds(CausalKnowledgeBase cKbase) { public void VisualizeHolds(CausalKnowledgeBase cKbase) { var causalKnowledgeBaseCopy = cKbase.clone(); - causalKnowledgeBaseCopy.addAll(this.getPremises()); + causalKnowledgeBaseCopy.addAll(this.getObservations()); var inducedAF = new InducedTheory(causalKnowledgeBaseCopy); - DungTheoryPlotter.plotFramework(inducedAF, 3000, 2000, "Premises: " + this.getPremises().toString() + " \n Conclusions: " + this.getConclusions().toString()); - } - - /** - * Helper method to check if a single conclusion is entailed by the premises in the given causal knowledge base. - * - * @param cKbase The causal knowledge base in which the conclusion is to be checked. - * @param conclusion The conclusion to check against the premises. - * @return true if the causal knowledge base entails the conclusion given the premises, otherwise false. - */ - private boolean checkStatement(CausalKnowledgeBase cKbase, PlFormula conclusion) { - return cKbase.entails(this.getPremises(), conclusion); + DungTheoryPlotter.plotFramework(inducedAF, 3000, 2000, "Premises: " + this.getObservations().toString() + " \n Conclusions: " + this.getConclusions().toString()); } } diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java index 73d5504dd..cbff8cd48 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java @@ -14,22 +14,22 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * -* Copyright 2023 The TweetyProject Team +* Copyright 2024 The TweetyProject Team */ package org.tweetyproject.arg.dung.causal.semantics; -import java.util.HashMap; -import java.util.HashSet; - import org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase; -import org.tweetyproject.arg.dung.causal.syntax.InducedTheory; -import org.tweetyproject.arg.dung.util.DungTheoryPlotter; +import org.tweetyproject.arg.dung.causal.syntax.StructuralCausalModel; import org.tweetyproject.logics.pl.syntax.PlFormula; import org.tweetyproject.logics.pl.syntax.Proposition; +import java.util.Collection; +import java.util.Map; + /** - * This class describes a counterfactual causal statement like: - * given phi, if v had been x then rho would be true + * This class describes a counterfactual causal statement wrt. some {@link CausalKnowledgeBase} of the form:
+ * "Given phi, if 'v' had been 'x' then psi would be true"
+ * where 'v' is some atom and 'x' is a truth value * * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm 'Argumentation-based Causal and Counterfactual Reasoning' 1st International Workshop on Argumentation for eXplainable AI, 2022" * @@ -38,53 +38,41 @@ public class CounterfactualStatement extends InterventionalStatement { /** - * Creates a new counterfactual causal statement. - * @param conclusions Conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. + * Initializes a new counterfactual causal statement. + * @param conclusions Conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. * @param interventions Maps explainable atoms to boolean values. - * @param premises PlFormulas which have to be true, so that the conclusions can be drawn. + * @param premises PlFormulas which have to be true, so that the conclusions can be drawn. */ - public CounterfactualStatement(HashSet conclusions, HashMap interventions, - HashSet premises) { + public CounterfactualStatement(Collection conclusions, Map interventions, Collection premises) { super(conclusions, interventions, premises); } @Override - public boolean holds(CausalKnowledgeBase cKbase) { - for(var conclusion : this.getConclusions()) { - if(!checkCounterFactualStatement(cKbase, conclusion)) { + public boolean holds(CausalKnowledgeBase ckbase) { + for(PlFormula conclusion : this.getConclusions()) { + if(!checkCounterFactualStatement(ckbase, conclusion)) { return false; } } - return true; } - @Override - public void VisualizeHolds(CausalKnowledgeBase cKbase) - { + /*@Override + public void VisualizeHolds(CausalKnowledgeBase cKbase) { var causalKnowledgeBaseCopy = getIntervenedTwinModel(cKbase); - causalKnowledgeBaseCopy.addAll(this.getPremises()); + causalKnowledgeBaseCopy.addAll(this.getObservations()); var inducedAF = new InducedTheory(causalKnowledgeBaseCopy); DungTheoryPlotter.plotFramework(inducedAF, 3000, 2000, - "Premises: " + this.getPremises().toString() + "Premises: " + this.getObservations().toString() + " \n Interventions: " + this.getInterventions().toString() + " \n Conclusions: " + this.getConclusions().toString()); - } + }*/ - private boolean checkCounterFactualStatement(CausalKnowledgeBase cKbase, PlFormula conclusion) { - var newKnowledgeBase = getIntervenedTwinModel(cKbase); - return newKnowledgeBase.entails(this.getPremises(), conclusion); - } - - protected CausalKnowledgeBase getIntervenedTwinModel(CausalKnowledgeBase cKbase) { - var twin = cKbase.getCausalModel().getTwinModel(); - var interventions = this.getInterventions(); - for(var expAtom : interventions.keySet()) { - twin.intervene( new Proposition(expAtom.getName() + "*"), interventions.get(expAtom).booleanValue()); + private boolean checkCounterFactualStatement(CausalKnowledgeBase ckbase, PlFormula conclusion) { + StructuralCausalModel twinModel = ckbase.getCausalModel().getTwinModel(); + for (Proposition atom : this.interventions.keySet()) { + twinModel = twinModel.intervene(new Proposition(atom.getName()+"*"), interventions.get(atom)); } - - var newKnowledgeBase = new CausalKnowledgeBase(twin, cKbase.getAssumptions()); - newKnowledgeBase.addAll(cKbase.getObservations()); - return newKnowledgeBase; + return new CausalKnowledgeBase(twinModel, ckbase.getAssumptions()).entails(this.getObservations(), conclusion); } } diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java index 0a8108763..47ce99dbb 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java @@ -14,57 +14,55 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * -* Copyright 2023 The TweetyProject Team +* Copyright 2024 The TweetyProject Team */ package org.tweetyproject.arg.dung.causal.semantics; -import java.util.HashMap; -import java.util.HashSet; - -import org.tweetyproject.arg.dung.causal.semantics.CausalStatement; import org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase; -import org.tweetyproject.arg.dung.causal.syntax.InducedTheory; -import org.tweetyproject.arg.dung.util.DungTheoryPlotter; import org.tweetyproject.logics.pl.syntax.PlFormula; import org.tweetyproject.logics.pl.syntax.Proposition; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + /** - * This class describes an interventional causal statement like: - * given phi, if v would be x then rho would be true + * This class describes an interventional causal statement wrt. some {@link CausalKnowledgeBase} of the form:
+ * "Given phi, if 'v' would be 'x' then psi would be true"
+ * where 'v' is some atom and 'x' is a truth value * * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm 'Argumentation-based Causal and Counterfactual Reasoning' 1st International Workshop on Argumentation for eXplainable AI, 2022" * * @author Julian Sander + * @author Lars Bengel */ public class InterventionalStatement extends CausalStatement { - private HashMap interventions; + protected Map interventions; /** - * Creates a new interventional causal statement. - * @param conclusions Conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. - * @param interventions Maps explainable atoms to boolean values. - * @param premises PlFormulas which have to be true, so that the conclusions can be drawn. + * Initializes a new interventional causal statement. + * + * @param conclusions Conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. + * @param interventions Maps explainable atoms to boolean values. + * @param observations PlFormulas which have to be true, so that the conclusions can be drawn. */ - public InterventionalStatement(HashSet conclusions, HashMap interventions, - HashSet premises) { - super(conclusions, premises); - + public InterventionalStatement(Collection conclusions, Map interventions, Collection observations) { + super(conclusions, observations); this.interventions = interventions; } /** * Retrieves the interventions of this causal statement. - * * @return A HashMap containing the interventions mapped from explainable atoms to their respective boolean values. */ - public HashMap getInterventions(){ - return new HashMap(this.interventions); + public Map getInterventions(){ + return new HashMap<>(this.interventions); } @Override - public boolean holds(CausalKnowledgeBase cKbase) { + public boolean holds(CausalKnowledgeBase ckbase) { for(var conclusion : this.getConclusions()) { - if(!checkInterventionalStatement(cKbase, conclusion)) { + if(!checkInterventionalStatement(ckbase, conclusion)) { return false; } } @@ -72,32 +70,23 @@ public boolean holds(CausalKnowledgeBase cKbase) { return true; } - @Override + /*@Override public void VisualizeHolds(CausalKnowledgeBase cKbase) { var causalKnowledgeBaseCopy = getIntervenedCopy(cKbase); - causalKnowledgeBaseCopy.addAll(this.getPremises()); + causalKnowledgeBaseCopy.addAll(this.getObservations()); var inducedAF = new InducedTheory(causalKnowledgeBaseCopy); DungTheoryPlotter.plotFramework(inducedAF, 3000, 2000, - "Premises: " + this.getPremises().toString() + "Premises: " + this.getObservations().toString() + " \n Interventions: " + this.getInterventions().toString() + " \n Conclusions: " + this.getConclusions().toString()); - } + }*/ - private boolean checkInterventionalStatement(CausalKnowledgeBase cKbase, PlFormula conclusion) { - var newKnowledgeBase = getIntervenedCopy(cKbase); - return newKnowledgeBase.entails(this.getPremises(), conclusion); - } - - protected CausalKnowledgeBase getIntervenedCopy(CausalKnowledgeBase cKbase) { - var interventions = this.getInterventions(); - var causalModel = cKbase.getCausalModel().clone(); - for(var expAtom : interventions.keySet()) { - causalModel.intervene(expAtom, interventions.get(expAtom).booleanValue()); + private boolean checkInterventionalStatement(CausalKnowledgeBase ckbase, PlFormula conclusion) { + CausalKnowledgeBase newBase = ckbase.clone(); + for (Proposition atom : interventions.keySet()) { + newBase = new CausalKnowledgeBase(newBase.getCausalModel().intervene(atom, interventions.get(atom)), ckbase.getAssumptions()); } - - var newKnowledgeBase = new CausalKnowledgeBase(causalModel, cKbase.getAssumptions()); - newKnowledgeBase.addAll(cKbase.getObservations()); - return newKnowledgeBase; + return newBase.entails(this.getObservations(), conclusion); } } diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java index a841884c1..e1139dbdb 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java +++ b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java @@ -42,6 +42,10 @@ public class CausalKnowledgeBase extends PlBeliefSet { /** The set of background assumptions */ protected Collection assumptions; + /** + * Initializes a causal knowledge from the given causal model without assumptions + * @param model some causal model + */ public CausalKnowledgeBase(StructuralCausalModel model) { this.model = model.clone(); this.assumptions = new HashSet<>(); @@ -65,6 +69,14 @@ public CausalKnowledgeBase(StructuralCausalModel model, Collection as } } + /** + * Initializes an empty causal knowledge base + */ + public CausalKnowledgeBase() { + this.model = new StructuralCausalModel(); + this.assumptions = new HashSet<>(); + } + /** * Adds an assumption to this knowledge base. * From 20be10e3da3f4e56ca2e15635e3d5d73319861df Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:58:42 +0200 Subject: [PATCH 05/43] moved to new package for causal reasoning --- .../ArgumentationBasedCausalReasoner.java | 31 ----------- .../dung/causal/reasoner/CausalReasoner.java | 17 ------ org-tweetyproject-causal/pom.xml | 30 +++++++++++ .../ArgumentationBasedCausalReasoner.java | 52 +++++++++++++++++++ ...umentationBasedCounterfactualReasoner.java | 2 +- .../causal/reasoner/CausalReasoner.java | 35 +++++++++++++ .../causal/semantics/CausalStatement.java | 6 +-- .../semantics/CounterfactualStatement.java | 6 +-- .../semantics/InterventionalStatement.java | 4 +- .../causal/syntax/CausalKnowledgeBase.java | 2 +- .../causal/syntax/InducedArgument.java | 3 +- .../causal/syntax/InducedTheory.java | 2 +- .../causal/syntax/StructuralCausalModel.java | 2 +- pom.xml | 1 + 14 files changed, 131 insertions(+), 62 deletions(-) delete mode 100644 org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java delete mode 100644 org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/CausalReasoner.java create mode 100644 org-tweetyproject-causal/pom.xml create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java rename {org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung => org-tweetyproject-causal/src/main/java/org/tweetyproject}/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java (53%) create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/CausalReasoner.java rename {org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung => org-tweetyproject-causal/src/main/java/org/tweetyproject}/causal/semantics/CausalStatement.java (94%) rename {org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung => org-tweetyproject-causal/src/main/java/org/tweetyproject}/causal/semantics/CounterfactualStatement.java (94%) rename {org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung => org-tweetyproject-causal/src/main/java/org/tweetyproject}/causal/semantics/InterventionalStatement.java (96%) rename {org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung => org-tweetyproject-causal/src/main/java/org/tweetyproject}/causal/syntax/CausalKnowledgeBase.java (99%) rename {org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung => org-tweetyproject-causal/src/main/java/org/tweetyproject}/causal/syntax/InducedArgument.java (96%) rename {org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung => org-tweetyproject-causal/src/main/java/org/tweetyproject}/causal/syntax/InducedTheory.java (99%) rename {org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung => org-tweetyproject-causal/src/main/java/org/tweetyproject}/causal/syntax/StructuralCausalModel.java (99%) diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java deleted file mode 100644 index 773f9816f..000000000 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCausalReasoner.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.tweetyproject.arg.dung.causal.reasoner; - -import org.tweetyproject.arg.dung.causal.semantics.CausalStatement; -import org.tweetyproject.arg.dung.causal.syntax.InducedTheory; -import org.tweetyproject.logics.pl.syntax.PlFormula; - -import java.util.Collection; - -public class ArgumentationBasedCausalReasoner { - - public InducedTheory getInducedTheory(Collection observations) { - // TODO incorporate observations into argument construction - // Utilise DeductiveArgumentation package ?? - return null; - } - public boolean query(CausalStatement statement) { - return true; - } - - public boolean query(Collection observations, PlFormula effect) { - return true; - } - - public Collection getConclusions(PlFormula observation) { - return null; - } - - public Collection getConclusions(Collection observations) { - return null; - } -} diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/CausalReasoner.java b/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/CausalReasoner.java deleted file mode 100644 index 34bd0b5a3..000000000 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/CausalReasoner.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.tweetyproject.arg.dung.causal.reasoner; - -import org.tweetyproject.logics.pl.syntax.PlFormula; - -import java.util.Collection; - -public abstract class CausalReasoner { - - protected Collection observations; - - /* - remove observations from CKB - CKB is a BeliefSet for the struct equations + a set of assumptions - observations only in the causal reasoner - causal reasoner has method to create AF, get conclusions, verify conclusions - */ -} diff --git a/org-tweetyproject-causal/pom.xml b/org-tweetyproject-causal/pom.xml new file mode 100644 index 000000000..1b8134ecb --- /dev/null +++ b/org-tweetyproject-causal/pom.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + causal + + TweetyProject (Causal Reasoning Library) + + + org.tweetyproject + parent-pom + 1.25-SNAPSHOT + .. + + + + org.tweetyproject.arg + dung + 1.25-SNAPSHOT + compile + + + org.tweetyproject.arg + deductive + 1.25-SNAPSHOT + compile + + + + diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java new file mode 100644 index 000000000..614c08125 --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java @@ -0,0 +1,52 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ +package org.tweetyproject.causal.reasoner; + +import org.tweetyproject.arg.deductive.syntax.DeductiveKnowledgeBase; +import org.tweetyproject.causal.semantics.CausalStatement; +import org.tweetyproject.causal.syntax.InducedTheory; +import org.tweetyproject.logics.pl.syntax.PlFormula; + +import java.util.Collection; + +public class ArgumentationBasedCausalReasoner { + + public InducedTheory getInducedTheory(Collection observations) { + + DeductiveKnowledgeBase knowledgeBase = new DeductiveKnowledgeBase(); + // TODO incorporate observations into argument construction + // Utilise DeductiveArgumentation package ?? + return null; + } + public boolean query(CausalStatement statement) { + return true; + } + + public boolean query(Collection observations, PlFormula effect) { + return true; + } + + public Collection getConclusions(PlFormula observation) { + return null; + } + + public Collection getConclusions(Collection observations) { + return null; + } +} diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java similarity index 53% rename from org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java rename to org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java index 96815c10c..c01772e21 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java @@ -1,4 +1,4 @@ -package org.tweetyproject.arg.dung.causal.reasoner; +package org.tweetyproject.causal.reasoner; public class ArgumentationBasedCounterfactualReasoner { } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/CausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/CausalReasoner.java new file mode 100644 index 000000000..fbc408cba --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/CausalReasoner.java @@ -0,0 +1,35 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ +package org.tweetyproject.causal.reasoner; + +import org.tweetyproject.logics.pl.syntax.PlFormula; + +import java.util.Collection; + +public abstract class CausalReasoner { + + protected Collection observations; + + /* + remove observations from CKB + CKB is a BeliefSet for the struct equations + a set of assumptions + observations only in the causal reasoner + causal reasoner has method to create AF, get conclusions, verify conclusions + */ +} diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java similarity index 94% rename from org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java rename to org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java index 3610906c0..b6d3159aa 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CausalStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java @@ -16,10 +16,10 @@ * * Copyright 2024 The TweetyProject Team */ -package org.tweetyproject.arg.dung.causal.semantics; +package org.tweetyproject.causal.semantics; -import org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase; -import org.tweetyproject.arg.dung.causal.syntax.InducedTheory; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.causal.syntax.InducedTheory; import org.tweetyproject.arg.dung.util.DungTheoryPlotter; import org.tweetyproject.logics.pl.syntax.PlFormula; diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java similarity index 94% rename from org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java rename to org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java index cbff8cd48..17a988a27 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/CounterfactualStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java @@ -16,10 +16,10 @@ * * Copyright 2024 The TweetyProject Team */ -package org.tweetyproject.arg.dung.causal.semantics; +package org.tweetyproject.causal.semantics; -import org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase; -import org.tweetyproject.arg.dung.causal.syntax.StructuralCausalModel; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.causal.syntax.StructuralCausalModel; import org.tweetyproject.logics.pl.syntax.PlFormula; import org.tweetyproject.logics.pl.syntax.Proposition; diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java similarity index 96% rename from org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java rename to org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java index 47ce99dbb..ee70d26a4 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/semantics/InterventionalStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java @@ -16,9 +16,9 @@ * * Copyright 2024 The TweetyProject Team */ -package org.tweetyproject.arg.dung.causal.semantics; +package org.tweetyproject.causal.semantics; -import org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.logics.pl.syntax.PlFormula; import org.tweetyproject.logics.pl.syntax.Proposition; diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java similarity index 99% rename from org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java rename to org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java index e1139dbdb..590188d9d 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBase.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java @@ -16,7 +16,7 @@ * * Copyright 2024 The TweetyProject Team */ -package org.tweetyproject.arg.dung.causal.syntax; +package org.tweetyproject.causal.syntax; import org.tweetyproject.logics.pl.reasoner.SimplePlReasoner; import org.tweetyproject.logics.pl.syntax.Negation; diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedArgument.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedArgument.java similarity index 96% rename from org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedArgument.java rename to org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedArgument.java index d3ce4f7ae..615adb17f 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedArgument.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedArgument.java @@ -16,11 +16,10 @@ * * Copyright 2024 The TweetyProject Team */ -package org.tweetyproject.arg.dung.causal.syntax; +package org.tweetyproject.causal.syntax; import java.util.Collection; import java.util.HashSet; -import java.util.Set; import org.tweetyproject.arg.dung.syntax.Argument; import org.tweetyproject.logics.pl.syntax.PlFormula; diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheory.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedTheory.java similarity index 99% rename from org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheory.java rename to org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedTheory.java index a11e7a9c8..6caac6993 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheory.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedTheory.java @@ -16,7 +16,7 @@ * * Copyright 2024 The TweetyProject Team */ -package org.tweetyproject.arg.dung.causal.syntax; +package org.tweetyproject.causal.syntax; import java.util.Collection; import java.util.HashSet; diff --git a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java similarity index 99% rename from org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/StructuralCausalModel.java rename to org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java index a92fb198c..e2527f96e 100644 --- a/org-tweetyproject-arg-dung/src/main/java/org/tweetyproject/arg/dung/causal/syntax/StructuralCausalModel.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java @@ -16,7 +16,7 @@ * * Copyright 2024 The TweetyProject Team */ -package org.tweetyproject.arg.dung.causal.syntax; +package org.tweetyproject.causal.syntax; import org.tweetyproject.commons.BeliefBase; import org.tweetyproject.commons.Signature; diff --git a/pom.xml b/pom.xml index ee9ece4c3..cb24a85b4 100644 --- a/pom.xml +++ b/pom.xml @@ -187,6 +187,7 @@ org-tweetyproject-action org-tweetyproject-beliefdynamics org-tweetyproject-cli + org-tweetyproject-causal org-tweetyproject-graphs org-tweetyproject-preferences org-tweetyproject-machinelearning From 05eff1951d70978e2ef4da1a60144d6804b854d0 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 15 Oct 2024 18:11:33 +0200 Subject: [PATCH 06/43] added example --- .../examples/CausalReasoningExample1.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java new file mode 100644 index 000000000..e365522e4 --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java @@ -0,0 +1,49 @@ +package org.tweetyproject.causal.examples; + +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.causal.syntax.StructuralCausalModel; +import org.tweetyproject.logics.pl.syntax.*; + +import java.util.Collection; +import java.util.HashSet; + +public class CausalReasoningExample1 { + public static void main(String[] args) throws StructuralCausalModel.CyclicDependencyException { + // Background atoms + Proposition corona = new Proposition("corona"); + Proposition influenza = new Proposition("influenza"); + Proposition atRisk = new Proposition("at-risk"); + + // Explainable atoms + Proposition covid = new Proposition("covid"); + Proposition flu = new Proposition("flu"); + Proposition shortOfBreath = new Proposition("short-of-breath"); + Proposition fever = new Proposition("fever"); + Proposition chills = new Proposition("chills"); + + // Construct Causal Model + StructuralCausalModel model = new StructuralCausalModel(); + // Add background atoms + model.addBackgroundAtom(covid); + model.addBackgroundAtom(influenza); + model.addBackgroundAtom(atRisk); + // Add structural equations + model.addExplainableAtom(covid, corona); + model.addExplainableAtom(flu, influenza); + model.addExplainableAtom(fever, new Disjunction(covid, flu)); + model.addExplainableAtom(chills, fever); + model.addExplainableAtom(shortOfBreath, new Conjunction(covid, atRisk)); + + // Define set of assumptions + Collection assumptions = new HashSet<>(); + assumptions.add(new Negation(corona)); + assumptions.add(new Negation(influenza)); + assumptions.add(new Negation(atRisk)); + assumptions.add(atRisk); + + // Initialize causal knowledge base + CausalKnowledgeBase kbase = new CausalKnowledgeBase(model, assumptions); + + // TODO + } +} From 8eb385695f280d28bb1f3235913aebc099ffa790 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 15 Oct 2024 18:41:02 +0200 Subject: [PATCH 07/43] some bugfixes --- .../examples/CausalReasoningExample1.java | 4 +- .../causal/syntax/CausalKnowledgeBase.java | 7 ++- .../causal/syntax/StructuralCausalModel.java | 54 ++++++++++++++----- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java index e365522e4..5a6188a57 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java @@ -24,7 +24,7 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend // Construct Causal Model StructuralCausalModel model = new StructuralCausalModel(); // Add background atoms - model.addBackgroundAtom(covid); + model.addBackgroundAtom(corona); model.addBackgroundAtom(influenza); model.addBackgroundAtom(atRisk); // Add structural equations @@ -44,6 +44,8 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend // Initialize causal knowledge base CausalKnowledgeBase kbase = new CausalKnowledgeBase(model, assumptions); + System.out.println(kbase); + // TODO } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java index 590188d9d..022f8b945 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java @@ -158,7 +158,12 @@ public Collection getSingleAtomConclusions(Collection prem } return conclusions; } - + + @Override + public String toString() { + return String.format("(%s, %s)", getCausalModel(), getAssumptions()); + } + @Override public CausalKnowledgeBase clone() { return new CausalKnowledgeBase(this.model, this.getAssumptions()); diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java index e2527f96e..0ff097785 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java @@ -31,9 +31,14 @@ * * @author Julian Sander * @author Lars Bengel + * + * TODO explainableAtoms needs to store somehow whether the atom is to be negated in the equation + * TODO or the equation must be transformed before storing */ public class StructuralCausalModel implements BeliefBase, Collection { + /** The background atoms of the causal model */ private Collection backgroundAtoms; + /** Explainable atoms and their causes */ private Map explainableAtoms; /** @@ -61,29 +66,37 @@ public StructuralCausalModel(Collection equations) throws CyclicDepen for (PlFormula formula : equations) { if (formula instanceof Equivalence) { Equivalence equation = (Equivalence) formula; - if (!equation.getFormulas().getFirst().isLiteral()) { + PlFormula left = equation.getFormulas().getFirst(); + if (!left.isLiteral()) { throw new IllegalArgumentException("Left side of Structural Equation must be literal!"); } - explainable.addAll(equation.getFormulas().getFirst().getAtoms()); + explainable.addAll(left.getAtoms()); atoms.addAll(equation.getAtoms()); - formulas.put(equation.getFormulas().getFirst(), equation.getFormulas().getSecond()); + formulas.put(left, equation.getFormulas().getSecond()); } else { throw new IllegalArgumentException("Structural Equations must be Equivalences"); } } atoms.removeAll(explainable); + + System.out.println(atoms); + System.out.println(explainable); + + // construct new model from equations this.addBackgroundAtoms(atoms); - while (!formulas.isEmpty()) { + Queue remaining = new ArrayDeque<>(explainable); + while (!remaining.isEmpty()) { boolean changed = false; - for (PlFormula lit : formulas.keySet()) { - try { - changed |= this.addExplainableAtom(lit, formulas.get(lit)); - formulas.remove(lit); - } catch (CyclicDependencyException ignored) { - } + + Proposition prop = remaining.poll(); + try { + changed |= this.addExplainableAtom(prop, formulas.get(prop)); + } catch (CyclicDependencyException ignored) { + remaining.add(prop); } - if (!changed) throw new CyclicDependencyException("The given set contains formulas with cyclic dependencies."); + //if (!changed) throw new CyclicDependencyException("The given set contains formulas with cyclic dependencies."); } + System.out.println(getExplainableAtoms()); } /** @@ -164,7 +177,9 @@ public boolean addExplainableAtom(PlFormula atom, PlFormula cause) throws Cyclic if (explainableAtoms.containsKey(prop)) throw new IllegalArgumentException("Atom already exists in the model"); if (cause.getAtoms().contains(prop)) throw new CyclicDependencyException("Causal relation cannot be cyclic. 'cause' must not contain variable."); - if (!getAtoms().containsAll(cause.getAtoms())) throw new CyclicDependencyException("Cause contains atoms that are not yet part of the causal model."); + if (!getAtoms().containsAll(cause.getAtoms())) { + throw new CyclicDependencyException("Cause contains atoms that are not yet part of the causal model."); + } PlFormula ret = explainableAtoms.put(prop, cause); return !cause.equals(ret); @@ -239,6 +254,21 @@ public StructuralCausalModel intervene(Proposition v, boolean x) { return newModel; } + public String prettyPrint() { + StringBuilder s = new StringBuilder(); + s.append("Background atoms: ").append(getBackgroundAtoms()).append("\n"); + s.append("Structural Equations:\n"); + for (Proposition atom : getExplainableAtoms()) { + s.append(String.format("%s<=> %s%n", atom, explainableAtoms.get(atom))); + } + return s.toString(); + } + + @Override + public String toString() { + return getStructuralEquations().toString(); + } + @Override public StructuralCausalModel clone() { try { From 49b5d4834bc1b204c761ee36871f58573cb69311 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 15 Oct 2024 18:54:03 +0200 Subject: [PATCH 08/43] some fixes --- .../syntax/CausalKnowledgeBaseTest.java | 152 ------------- .../dung/causal/syntax/CausalModelTest.java | 213 ------------------ .../syntax/CounterfactualStatementTest.java | 115 ---------- .../dung/causal/syntax/InducedTheoryTest.java | 202 ----------------- .../syntax/InterventionalStatementTest.java | 92 -------- .../examples/CausalReasoningExample1.java | 15 +- .../causal/syntax/StructuralCausalModel.java | 9 +- 7 files changed, 7 insertions(+), 791 deletions(-) delete mode 100644 org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBaseTest.java delete mode 100644 org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalModelTest.java delete mode 100644 org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CounterfactualStatementTest.java delete mode 100644 org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheoryTest.java delete mode 100644 org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InterventionalStatementTest.java diff --git a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBaseTest.java b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBaseTest.java deleted file mode 100644 index 9c7caf7f8..000000000 --- a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalKnowledgeBaseTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file is part of "TweetyProject", a collection of Java libraries for - * logical aspects of artificial intelligence and knowledge representation. - * - * TweetyProject is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * Copyright 2023 The TweetyProject Team - */ -package org.tweetyproject.arg.dung.causal.syntax; - -import java.util.HashSet; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.tweetyproject.logics.pl.syntax.Conjunction; -import org.tweetyproject.logics.pl.syntax.Disjunction; -import org.tweetyproject.logics.pl.syntax.Equivalence; -import org.tweetyproject.logics.pl.syntax.Negation; -import org.tweetyproject.logics.pl.syntax.PlFormula; -import org.tweetyproject.logics.pl.syntax.Proposition; - -/** - * @author Julian Sander - * @version TweetyProject 1.23 - * - */ -class CausalKnowledgeBaseTest { - - - /** - * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase#CausalKnowledgeBase(CausalModel, java.util.Set)}. - */ - @Test - void testCausalKnowledgeBase() { - //Arrange - var covid = new Proposition("covid"); - var corona = new Proposition("corona"); - var shortOfBreath = new Proposition("short-of-breath"); - var atRisk = new Proposition("at-risk"); - var influenza = new Proposition("influenza"); - var fever = new Proposition("fever"); - var negInfluenza = new Negation(influenza); - var causalModel = setupModel(covid, corona, shortOfBreath, atRisk, influenza, fever); - var causalKnowledgeBase = setup(corona, atRisk, negInfluenza, causalModel); - var notLiteral = new Conjunction(negInfluenza, corona); - var notBackGroundAtom = new Proposition("this is not a background-atom"); - - - //Assert - var assumptions2 = causalKnowledgeBase.getAssumptions(); - assumptions2.remove(negInfluenza); //simulate missing assumption for background atom - Assertions.assertThrowsExactly(IllegalArgumentException.class, () - -> new CausalKnowledgeBase(causalModel, assumptions2)); - var assumptions3 = causalKnowledgeBase.getAssumptions(); - assumptions3.add(negInfluenza); - assumptions3.add(notLiteral); //simulate some assumptions being no literal - Assertions.assertThrowsExactly(IllegalArgumentException.class, () - -> new CausalKnowledgeBase(causalModel, assumptions3)); - var assumptions4 = causalKnowledgeBase.getAssumptions(); - assumptions4.remove(notLiteral); - assumptions4.add(notBackGroundAtom); //simulate assumption anything else than a background-atom - Assertions.assertThrowsExactly(IllegalArgumentException.class, () - -> new CausalKnowledgeBase(causalModel, assumptions4)); - } - - /** - * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase#entails(java.util.Set, org.tweetyproject.logics.pl.syntax.PlFormula)}. - */ - @Test - void testEntails() { - //Arrange - var covid = new Proposition("covid"); - var corona = new Proposition("corona"); - var shortOfBreath = new Proposition("short-of-breath"); - var atRisk = new Proposition("at-risk"); - var influenza = new Proposition("influenza"); - var fever = new Proposition("fever"); - var negInfluenza = new Negation(influenza); - var causalModel = setupModel(covid, corona, shortOfBreath, atRisk, influenza, fever); - var causalKnowledgeBase = setup(corona, atRisk, negInfluenza, causalModel); - - //Act - var premises = new HashSet(); - premises.add(new Conjunction(new Negation(corona), fever)); - Assertions.assertTrue(causalKnowledgeBase.entails(premises, influenza)); - } - - /** - * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase#getSingleAtomConclusions(java.util.Set)}. - */ - @Test - void testGetConclusions() { - //Arrange - var covid = new Proposition("covid"); - var corona = new Proposition("corona"); - var shortOfBreath = new Proposition("short-of-breath"); - var atRisk = new Proposition("at-risk"); - var influenza = new Proposition("influenza"); - var fever = new Proposition("fever"); - var negInfluenza = new Negation(influenza); - var causalModel = setupModel(covid, corona, shortOfBreath, atRisk, influenza, fever); - var causalKnowledgeBase = setup(corona, atRisk, negInfluenza, causalModel); - - //Act - var premises = new HashSet(); - premises.add(new Conjunction(new Negation(corona), fever)); - Assertions.assertTrue(causalKnowledgeBase.getSingleAtomConclusions(premises).contains(influenza)); - } - - private CausalModel setupModel(Proposition covid, Proposition corona, Proposition shortOfBreath, Proposition atRisk, Proposition influenza, Proposition fever) { - - var flu = new Proposition("flu"); - - var chills = new Proposition("chills"); - - var eq1 = new Equivalence(covid, corona); - var eq2 = new Equivalence(flu, influenza); - var eq3 = new Equivalence(fever, new Disjunction(covid, flu)); - var eq4 = new Equivalence(chills, fever); - var eq5 = new Equivalence(shortOfBreath, new Conjunction(covid, atRisk)); - var eqs = new HashSet(); - eqs.add(eq1); - eqs.add(eq2); - eqs.add(eq3); - eqs.add(eq4); - eqs.add(eq5); - return new CausalModel(eqs); - } - - private CausalKnowledgeBase setup(Proposition corona, Proposition atRisk, Negation negInfluenza, CausalModel causalModel) { - // Assumptions - var negAtRisk = new Negation(atRisk); - var negCorona = new Negation(corona); - var assumptions = new HashSet(); - assumptions.add(atRisk); - assumptions.add(negAtRisk); - assumptions.add(negCorona); - assumptions.add(negInfluenza); - - return new CausalKnowledgeBase(causalModel, assumptions); - } -} diff --git a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalModelTest.java b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalModelTest.java deleted file mode 100644 index 4bd6d6d46..000000000 --- a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CausalModelTest.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * This file is part of "TweetyProject", a collection of Java libraries for - * logical aspects of artificial intelligence and knowledge representation. - * - * TweetyProject is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * Copyright 2023 The TweetyProject Team - */ -package org.tweetyproject.arg.dung.causal.syntax; - -import java.util.HashSet; - -import org.junit.Assert; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.tweetyproject.logics.pl.syntax.Conjunction; -import org.tweetyproject.logics.pl.syntax.Disjunction; -import org.tweetyproject.logics.pl.syntax.Equivalence; -import org.tweetyproject.logics.pl.syntax.Negation; -import org.tweetyproject.logics.pl.syntax.PlFormula; -import org.tweetyproject.logics.pl.syntax.Proposition; - -/** - * @author Julian Sander - * @version TweetyProject 1.23 - * - */ -class CausalModelTest { - - @Test - void testGetTwinModel() { - //Arrange - var bg1 = new Proposition("bg1"); - var bg2 = new Proposition("bg2"); - var exp1 = new Proposition("exp1"); - var exp2 = new Proposition("exp2"); - var eq1 = new Equivalence(exp1, bg1); - var eq2 = new Equivalence(exp2, new Conjunction(exp1, bg2)); - var eqs = new HashSet(); - eqs.add(eq1); - eqs.add(eq2); - var causalModel = new CausalModel(eqs); - - //Act - var twin = causalModel.getTwinModel(); - - //Assert - Assertions.assertEquals(4, twin.getExplainableAtoms().size()); - Assertions.assertEquals(4, twin.getStructuralEquations().size()); - Assertions.assertTrue(twin.getExplainableAtoms().contains(exp1)); - Assertions.assertTrue(twin.getExplainableAtoms().contains(exp2)); - for(var eq : twin.getStructuralEquations()) { - if(eq.getFormulas().getFirst().getAtoms().contains(exp1) - ) { - Assertions.assertEquals(eq, eq1); - }else if(eq.getFormulas().getFirst().getAtoms().contains(exp2)) { - Assertions.assertEquals(eq, eq2); - } - else if(eq.getFormulas().getFirst().getAtoms().contains(new Proposition("exp1*"))) - { - Assertions.assertFalse(eq.getAtoms().contains(exp1)); - Assertions.assertTrue(eq.getAtoms().contains(bg1)); - }else if(eq.getFormulas().getFirst().getAtoms().contains(new Proposition("exp2*"))){ - Assertions.assertFalse(eq.getAtoms().contains(exp2)); - Assertions.assertTrue(eq.getAtoms().contains(bg2)); - Assertions.assertTrue(eq.getAtoms().contains(new Proposition("exp1*"))); - }else { - Assertions.fail(); - } - } - } - - @Test - void testIntervene() { - //Arrange - var corona = new Proposition("corona"); - var influenza = new Proposition("influenza"); - var atRisk = new Proposition("at-risk"); - - var covid = new Proposition("covid"); - var flu = new Proposition("flu"); - var shortOfBreath = new Proposition("short-of-breath"); - var fever = new Proposition("fever"); - var chills = new Proposition("chills"); - - var eq1 = new Equivalence(covid, corona); - var eq2 = new Equivalence(flu, influenza); - var eq3 = new Equivalence(fever, new Disjunction(covid, flu)); - var eq4 = new Equivalence(chills, fever); - var eq5 = new Equivalence(shortOfBreath, new Conjunction(covid, atRisk)); - var eqs = new HashSet(); - eqs.add(eq1); - eqs.add(eq2); - eqs.add(eq3); - eqs.add(eq4); - eqs.add(eq5); - var causalModel = new CausalModel(eqs); - - var negAtRisk = new Negation(atRisk); - var negCorona = new Negation(corona); - var negInfluenza = new Negation(influenza); - var assumptions = new HashSet(); - assumptions.add(atRisk); - assumptions.add(negAtRisk); - assumptions.add(negCorona); - assumptions.add(negInfluenza); - var knowledgeBase = new CausalKnowledgeBase(causalModel, assumptions); - - //Act - var premises = new HashSet(); - premises.add(shortOfBreath); - - //Assert - Assertions.assertTrue(knowledgeBase.entails(premises, chills)); - - causalModel.intervene(fever, false); - Assertions.assertFalse(knowledgeBase.entails(premises, chills)); - Assertions.assertTrue(knowledgeBase.entails(premises, new Negation(chills))); - - } - - /** - * Test method for {@link CausalModel#CausalModel(java.util.Set, java.util.Set, java.util.Set)}. - * Test method for {@link CausalModel#CausalModel(java.util.Set)}. - */ - @Test - void testCausalModel() { - - //Arrange - var corona = new Proposition("corona"); - var influenza = new Proposition("influenza"); - var atRisk = new Proposition("at-risk"); - - var covid = new Proposition("covid"); - var flu = new Proposition("flu"); - var shortOfBreath = new Proposition("short-of-breath"); - var fever = new Proposition("fever"); - var chills = new Proposition("chills"); - - var eq1 = new Equivalence(covid, corona); - var eq2 = new Equivalence(flu, influenza); - var eq3 = new Equivalence(fever, new Disjunction(covid, flu)); - var eq4 = new Equivalence(chills, fever); - var eq5 = new Equivalence(shortOfBreath, new Conjunction(covid, atRisk)); - var wrongEq3 = new Equivalence(fever, new Disjunction(covid, fever)); - var missingFluEq2 = new Equivalence(fever, influenza); - var doubleFeverEq4 = new Equivalence(fever, new Conjunction(covid, atRisk)); - - //Act - var model1 = BuildFirstOption(corona, influenza, atRisk, covid, flu, shortOfBreath, fever, chills, eq1, eq2, eq3, eq4, eq5); - var model2 = BuildSecondOption(eq1, eq2, eq3, eq4, eq5); - - //Assure - Assert.assertEquals(model1.hashCode(), model2.hashCode()); - // Assert.assertEquals(model1, model2); - Assertions.assertThrowsExactly(IllegalArgumentException.class, () - -> BuildFirstOption(corona, influenza, atRisk, covid, flu, shortOfBreath, fever, chills, eq1, eq2, wrongEq3, eq4, eq5)); - Assertions.assertThrowsExactly(IllegalArgumentException.class, () - -> BuildFirstOption(corona, influenza, atRisk, covid, flu, shortOfBreath, fever, chills, eq1, missingFluEq2, eq3, eq4, eq5)); //simulates missing eq for one explainable atom - Assertions.assertThrowsExactly(IllegalArgumentException.class, () - -> BuildFirstOption(corona, influenza, atRisk, covid, flu, shortOfBreath, fever, chills, eq1, eq2, eq3, doubleFeverEq4, eq5)); //simulates having more than one eq for a explainable atim - // Assertions.assertEquals(model1, model2); - } - - private CausalModel BuildFirstOption(Proposition corona, Proposition influenza, Proposition atRisk, Proposition covid, - Proposition flu, Proposition shortOfBreath, Proposition fever, Proposition chills, - Equivalence eq1, Equivalence eq2, Equivalence eq3, Equivalence eq4, Equivalence eq5) { - - var u = new HashSet(); - u.add(corona); - u.add(influenza); - u.add(atRisk); - - var v = new HashSet(); - v.add(covid); - v.add(flu); - v.add(shortOfBreath); - v.add(fever); - v.add(chills); - - var eqs = new HashSet(); - eqs.add(eq1); - eqs.add(eq2); - eqs.add(eq3); - eqs.add(eq4); - eqs.add(eq5); - - return new CausalModel(u, v, eqs); - - } - - private CausalModel BuildSecondOption(Equivalence eq1, Equivalence eq2, Equivalence eq3, Equivalence eq4, Equivalence eq5) { - var eqs = new HashSet(); - eqs.add(eq1); - eqs.add(eq2); - eqs.add(eq3); - eqs.add(eq4); - eqs.add(eq5); - - return new CausalModel(eqs); - } - -} diff --git a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CounterfactualStatementTest.java b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CounterfactualStatementTest.java deleted file mode 100644 index 1333fa646..000000000 --- a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/CounterfactualStatementTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* -* This file is part of "TweetyProject", a collection of Java libraries for -* logical aspects of artificial intelligence and knowledge representation. -* -* TweetyProject is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License version 3 as -* published by the Free Software Foundation. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -* -* Copyright 2023 The TweetyProject Team -*/ -package org.tweetyproject.arg.dung.causal.syntax; - -import java.util.HashMap; -import java.util.HashSet; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.tweetyproject.arg.dung.causal.semantics.CounterfactualStatement; -import org.tweetyproject.logics.pl.syntax.Conjunction; -import org.tweetyproject.logics.pl.syntax.Disjunction; -import org.tweetyproject.logics.pl.syntax.Equivalence; -import org.tweetyproject.logics.pl.syntax.Negation; -import org.tweetyproject.logics.pl.syntax.PlFormula; -import org.tweetyproject.logics.pl.syntax.Proposition; - -/** - * @author User - * - */ -class CounterfactualStatementTest { - - /** - * Test method for {@link CounterfactualStatement#holds(org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase)}. - */ - @Test - void testHolds() { - //Arrange - var covid = new Proposition("covid"); - var corona = new Proposition("corona"); - var shortOfBreath = new Proposition("short-of-breath"); - var atRisk = new Proposition("at-risk"); - var influenza = new Proposition("influenza"); - var fever = new Proposition("fever"); - var negInfluenza = new Negation(influenza); - var causalModel = setupModel(covid, corona, shortOfBreath, atRisk, influenza, fever); - var causalKnowledgeBase = setup(corona, atRisk, negInfluenza, causalModel); - - //Act - var premises = new HashSet(); - premises.add(fever); - var interventions = new HashMap(); - interventions.put(covid, false); - var feverCopy = new Proposition("fever*"); - var conclusions1 = new HashSet(); - conclusions1.add(feverCopy); - var counterfactualStatement1 = new CounterfactualStatement(conclusions1, interventions, premises); - var conclusions2 = new HashSet(); - conclusions2.add( new Negation(feverCopy)); - var counterfactualStatement2 = new CounterfactualStatement(conclusions2, interventions, premises); - - //Assert - Assertions.assertFalse(counterfactualStatement1.holds(causalKnowledgeBase)); - Assertions.assertFalse(counterfactualStatement2.holds(causalKnowledgeBase)); - - //Act - var premises2 = new HashSet(); - premises2.add(new Conjunction(fever, shortOfBreath)); - var counterfactualStatement3 = new CounterfactualStatement(conclusions1, interventions, premises2); - var counterfactualStatement4 = new CounterfactualStatement(conclusions2, interventions, premises2); - Assertions.assertFalse(counterfactualStatement3.holds(causalKnowledgeBase)); - Assertions.assertFalse(counterfactualStatement4.holds(causalKnowledgeBase)); - } - - private CausalModel setupModel(Proposition covid, Proposition corona, Proposition shortOfBreath, Proposition atRisk, Proposition influenza, Proposition fever) { - - var flu = new Proposition("flu"); - - var chills = new Proposition("chills"); - - var eq1 = new Equivalence(covid, corona); - var eq2 = new Equivalence(flu, influenza); - var eq3 = new Equivalence(fever, new Disjunction(covid, flu)); - var eq4 = new Equivalence(chills, fever); - var eq5 = new Equivalence(shortOfBreath, new Conjunction(covid, atRisk)); - var eqs = new HashSet(); - eqs.add(eq1); - eqs.add(eq2); - eqs.add(eq3); - eqs.add(eq4); - eqs.add(eq5); - return new CausalModel(eqs); - } - - private CausalKnowledgeBase setup(Proposition corona, Proposition atRisk, Negation negInfluenza, CausalModel causalModel) { - // Assumptions - var negAtRisk = new Negation(atRisk); - var negCorona = new Negation(corona); - var assumptions = new HashSet(); - assumptions.add(atRisk); - assumptions.add(negAtRisk); - assumptions.add(negCorona); - assumptions.add(negInfluenza); - - return new CausalKnowledgeBase(causalModel, assumptions); - } - -} diff --git a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheoryTest.java b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheoryTest.java deleted file mode 100644 index 6cb892fb3..000000000 --- a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InducedTheoryTest.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * This file is part of "TweetyProject", a collection of Java libraries for - * logical aspects of artificial intelligence and knowledge representation. - * - * TweetyProject is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * Copyright 2023 The TweetyProject Team - */ -package org.tweetyproject.arg.dung.causal.syntax; - -import java.util.HashSet; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.tweetyproject.arg.dung.reasoner.AbstractExtensionReasoner; -import org.tweetyproject.arg.dung.semantics.Semantics; -import org.tweetyproject.arg.dung.syntax.Argument; -import org.tweetyproject.logics.pl.syntax.Conjunction; -import org.tweetyproject.logics.pl.syntax.Disjunction; -import org.tweetyproject.logics.pl.syntax.Equivalence; -import org.tweetyproject.logics.pl.syntax.Negation; -import org.tweetyproject.logics.pl.syntax.PlFormula; -import org.tweetyproject.logics.pl.syntax.Proposition; - -/** - * @author Julian Sander - * - */ -class InducedTheoryTest { - - /** - * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.InducedTheory#addAttack(org.tweetyproject.arg.dung.syntax.Argument, org.tweetyproject.arg.dung.syntax.Argument)}. - */ - @Test - void testAddAttack() { - //Arrange - var corona = new Proposition("corona"); - var influenza = new Proposition("influenza"); - var fever = new Proposition("fever"); - var shortOfBreath = new Proposition("short-of-breath"); - var negInfluenza = new Negation(influenza); - var causalKnowledgeBase = setup(corona, influenza, fever, shortOfBreath, negInfluenza); - var framework = new InducedTheory(causalKnowledgeBase); - var args = framework.getArguments(); - var attacker = args.iterator().next(); - InducedArgument victim = null; - - for(var arg : args) { - if(!arg.getPremises().contains(attacker.getConclusion().complement())) { - victim = arg; - break; - } - } - - //Act - //Assert - InducedArgument victim2 = victim; - Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> framework.addAttack(attacker, victim2)); - Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> framework.addAttack((Argument)attacker, (Argument) victim2)); - } - - /** - * Test method for {@link org.tweetyproject.arg.dung.causal.syntax.InducedTheory#add(org.tweetyproject.arg.dung.syntax.Argument)}. - */ - @Test - void testAddArgument() { - //Arrange - var corona = new Proposition("corona"); - var influenza = new Proposition("influenza"); - var fever = new Proposition("fever"); - var shortOfBreath = new Proposition("short-of-breath"); - var negInfluenza = new Negation(influenza); - var causalKnowledgeBase = setup(corona, influenza, fever, shortOfBreath, negInfluenza); - var framework = new InducedTheory(causalKnowledgeBase); - var a0 = new Argument("a0"); - - //Act - //Assert - Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> framework.add(a0)); - } - - /** - * Test method for @see org.tweetyproject.arg.dung.causal.syntax.InducedTheory. - */ - @Test - void testCausalTheory() { - //Arrange - var corona = new Proposition("corona"); - var influenza = new Proposition("influenza"); - var fever = new Proposition("fever"); - var shortOfBreath = new Proposition("short-of-breath"); - var negInfluenza = new Negation(influenza); - var causalKnowledgeBase = setup(corona, influenza, fever, shortOfBreath, negInfluenza); - - //Test entailment of causal knowledge base - var premises = new HashSet(); - premises.add(fever); - Assertions.assertFalse(causalKnowledgeBase.entails(premises, shortOfBreath)); - var negShortOfBreath = new Negation(shortOfBreath); - Assertions.assertFalse(causalKnowledgeBase.entails(premises, negShortOfBreath)); - - //Act - causalKnowledgeBase.add(fever); - var framework = new InducedTheory(causalKnowledgeBase); - var reasoner = AbstractExtensionReasoner.getSimpleReasonerForSemantics(Semantics.ST); - var extensions = reasoner.getModels(framework); - - boolean allExtConcludeShortOfBreath = true; - boolean allExtConcludeNotShortOfBreath = true; - for(var ext : extensions) { - boolean hasConclusionShortOfBreath = false; - boolean hasConclusionNotShortOfBreath = false; - for(var argument : ext) { - if(((InducedArgument) argument).getConclusion().equals(shortOfBreath)) { - hasConclusionShortOfBreath = true; - } - if(((InducedArgument) argument).getConclusion().equals(negShortOfBreath)) { - hasConclusionNotShortOfBreath = true; - } - } - if(!hasConclusionShortOfBreath) { - allExtConcludeShortOfBreath = false; - } - if(!hasConclusionNotShortOfBreath) { - allExtConcludeNotShortOfBreath = false; - } - } - - //Assert - Assertions.assertFalse(allExtConcludeShortOfBreath); - Assertions.assertFalse(allExtConcludeNotShortOfBreath); - - //Test entailment of causal knowledge base - var causalKnowledgeBase2 = setup(corona, influenza, fever, shortOfBreath, negInfluenza); - var premises2 = new HashSet(); - var formula = new Conjunction(new Negation(corona), fever); - premises2.add(formula); - Assertions.assertTrue(causalKnowledgeBase2.getSingleAtomConclusions(premises2).contains(influenza)); - - //Act - causalKnowledgeBase2.add(formula); - var framework2 = new InducedTheory(causalKnowledgeBase2); - var extensions2 = reasoner.getModels(framework2); - - boolean allExtConcludeInfluenza = true; - for(var ext : extensions2) { - boolean hasConclusionInfluenza = false; - for(var argument : ext) { - if(((InducedArgument) argument).getConclusion().equals(influenza)) { - hasConclusionInfluenza = true; - } - } - if(!hasConclusionInfluenza) { - allExtConcludeInfluenza = false; - } - } - Assertions.assertTrue(allExtConcludeInfluenza); - } - - private CausalKnowledgeBase setup(Proposition corona, Proposition influenza, Proposition fever, Proposition shortOfBreath, Negation negInfluenza) { - //causal model - var atRisk = new Proposition("at-risk"); - - var covid = new Proposition("covid"); - var flu = new Proposition("flu"); - var chills = new Proposition("chills"); - - var eq1 = new Equivalence(covid, corona); - var eq2 = new Equivalence(flu, influenza); - var eq3 = new Equivalence(fever, new Disjunction(covid, flu)); - var eq4 = new Equivalence(chills, fever); - var eq5 = new Equivalence(shortOfBreath, new Conjunction(covid, atRisk)); - var eqs = new HashSet(); - eqs.add(eq1); - eqs.add(eq2); - eqs.add(eq3); - eqs.add(eq4); - eqs.add(eq5); - var causalModel = new CausalModel(eqs); - - // Assumptions - var negAtRisk = new Negation(atRisk); - var negCorona = new Negation(corona); - var assumptions = new HashSet(); - assumptions.add(atRisk); - assumptions.add(negAtRisk); - assumptions.add(negCorona); - assumptions.add(negInfluenza); - - return new CausalKnowledgeBase(causalModel, assumptions); - } -} diff --git a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InterventionalStatementTest.java b/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InterventionalStatementTest.java deleted file mode 100644 index d79758752..000000000 --- a/org-tweetyproject-arg-dung/src/test/java/org/tweetyproject/arg/dung/causal/syntax/InterventionalStatementTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file is part of "TweetyProject", a collection of Java libraries for - * logical aspects of artificial intelligence and knowledge representation. - * - * TweetyProject is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * Copyright 2023 The TweetyProject Team - */ -package org.tweetyproject.arg.dung.causal.syntax; - -import java.util.HashMap; -import java.util.HashSet; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.tweetyproject.arg.dung.causal.semantics.InterventionalStatement; -import org.tweetyproject.logics.pl.syntax.Conjunction; -import org.tweetyproject.logics.pl.syntax.Disjunction; -import org.tweetyproject.logics.pl.syntax.Equivalence; -import org.tweetyproject.logics.pl.syntax.Negation; -import org.tweetyproject.logics.pl.syntax.PlFormula; -import org.tweetyproject.logics.pl.syntax.Proposition; - -/** - * @author User - * - */ -class InterventionalStatementTest { - - /** - * Test method for {@link InterventionalStatement#holds(org.tweetyproject.arg.dung.causal.syntax.CausalKnowledgeBase)}. - */ - @Test - void testHolds() { - //Arrange - var corona = new Proposition("corona"); - var influenza = new Proposition("influenza"); - var atRisk = new Proposition("at-risk"); - - var covid = new Proposition("covid"); - var flu = new Proposition("flu"); - var shortOfBreath = new Proposition("short-of-breath"); - var fever = new Proposition("fever"); - var chills = new Proposition("chills"); - - var eq1 = new Equivalence(covid, corona); - var eq2 = new Equivalence(flu, influenza); - var eq3 = new Equivalence(fever, new Disjunction(covid, flu)); - var eq4 = new Equivalence(chills, fever); - var eq5 = new Equivalence(shortOfBreath, new Conjunction(covid, atRisk)); - var eqs = new HashSet(); - eqs.add(eq1); - eqs.add(eq2); - eqs.add(eq3); - eqs.add(eq4); - eqs.add(eq5); - var causalModel = new CausalModel(eqs); - - var negAtRisk = new Negation(atRisk); - var negCorona = new Negation(corona); - var negInfluenza = new Negation(influenza); - var assumptions = new HashSet(); - assumptions.add(atRisk); - assumptions.add(negAtRisk); - assumptions.add(negCorona); - assumptions.add(negInfluenza); - var knowledgeBase = new CausalKnowledgeBase(causalModel, assumptions); - - //Act - var premises = new HashSet(); - premises.add(shortOfBreath); - var interventions = new HashMap(); - interventions.put(fever, false); - var conclusions = new HashSet(); - conclusions.add(new Negation(chills)); - var interventionalStatement = new InterventionalStatement(conclusions, interventions, premises); - - //Assert - Assertions.assertTrue(interventionalStatement.holds(knowledgeBase)); - } - -} diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java index 5a6188a57..64b4e0cdf 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java @@ -34,18 +34,15 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend model.addExplainableAtom(chills, fever); model.addExplainableAtom(shortOfBreath, new Conjunction(covid, atRisk)); - // Define set of assumptions - Collection assumptions = new HashSet<>(); - assumptions.add(new Negation(corona)); - assumptions.add(new Negation(influenza)); - assumptions.add(new Negation(atRisk)); - assumptions.add(atRisk); - // Initialize causal knowledge base - CausalKnowledgeBase kbase = new CausalKnowledgeBase(model, assumptions); + CausalKnowledgeBase kbase = new CausalKnowledgeBase(model); + // Add assumptions + kbase.addAssumption(new Negation(corona)); + kbase.addAssumption(new Negation(influenza)); + kbase.addAssumption(new Negation(atRisk)); + kbase.addAssumption(atRisk); System.out.println(kbase); - // TODO } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java index 0ff097785..c11cc29f2 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java @@ -79,24 +79,17 @@ public StructuralCausalModel(Collection equations) throws CyclicDepen } atoms.removeAll(explainable); - System.out.println(atoms); - System.out.println(explainable); - // construct new model from equations this.addBackgroundAtoms(atoms); Queue remaining = new ArrayDeque<>(explainable); while (!remaining.isEmpty()) { - boolean changed = false; - Proposition prop = remaining.poll(); try { - changed |= this.addExplainableAtom(prop, formulas.get(prop)); + this.addExplainableAtom(prop, formulas.get(prop)); } catch (CyclicDependencyException ignored) { remaining.add(prop); } - //if (!changed) throw new CyclicDependencyException("The given set contains formulas with cyclic dependencies."); } - System.out.println(getExplainableAtoms()); } /** From 59b8a4378ace79734b8055c5852ec09e69b95304 Mon Sep 17 00:00:00 2001 From: Lars Bengel Date: Wed, 16 Oct 2024 14:21:10 +0200 Subject: [PATCH 09/43] updated dependencies --- org-tweetyproject-causal/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org-tweetyproject-causal/pom.xml b/org-tweetyproject-causal/pom.xml index 1b8134ecb..65b7f3e43 100644 --- a/org-tweetyproject-causal/pom.xml +++ b/org-tweetyproject-causal/pom.xml @@ -20,8 +20,8 @@ compile - org.tweetyproject.arg - deductive + org.tweetyproject.logics + pl 1.25-SNAPSHOT compile From f0b0646443aef6fc4475ab66563d23a0b61cac40 Mon Sep 17 00:00:00 2001 From: Lars Bengel Date: Wed, 16 Oct 2024 14:21:47 +0200 Subject: [PATCH 10/43] implemented causal reasoner --- .../reasoner/AbstractCausalReasoner.java | 110 +++++++++++ .../ArgumentationBasedCausalReasoner.java | 149 +++++++++++++-- .../causal/reasoner/CausalReasoner.java | 35 ---- .../causal/semantics/CausalStatement.java | 20 +- .../semantics/CounterfactualStatement.java | 12 +- .../semantics/InterventionalStatement.java | 13 +- ...ducedArgument.java => CausalArgument.java} | 6 +- .../causal/syntax/CausalKnowledgeBase.java | 41 +--- .../causal/syntax/InducedTheory.java | 177 ------------------ 9 files changed, 275 insertions(+), 288 deletions(-) create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java delete mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/CausalReasoner.java rename org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/{InducedArgument.java => CausalArgument.java} (91%) delete mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedTheory.java diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java new file mode 100644 index 000000000..0e315ed4e --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java @@ -0,0 +1,110 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ +package org.tweetyproject.causal.reasoner; + +import org.tweetyproject.causal.semantics.CausalStatement; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.commons.QualitativeReasoner; +import org.tweetyproject.logics.pl.syntax.PlBeliefSet; +import org.tweetyproject.logics.pl.syntax.PlFormula; + +import java.util.Collection; +import java.util.HashSet; + +/** + * General abstract reasoner for basic causal reasoning with {@link CausalKnowledgeBase} + * + * @author Lars Bengel + */ +public abstract class AbstractCausalReasoner implements QualitativeReasoner { + + /** + * Determines whether the given effect is entailed by the causal knowledge base together with the observations + * + * @param cbase some causal knowledge base + * @param observations some logical formulae over atoms of the causal knowledge base + * @param effect some logical formula over atoms of the causal knowledge base + * @return TRUE iff the causal knowledge base together with the observations logically entails the effect + */ + public abstract boolean query(CausalKnowledgeBase cbase, Collection observations, PlFormula effect); + + /** + * Determines whether the given effect is entailed by the causal knowledge base together with the observation + * + * @param cbase some causal knowledge base + * @param observation some logical formula over atoms of the causal knowledge base + * @param effect some logical formula over atoms of the causal knowledge base + * @return TRUE iff the causal knowledge base together with the observation logically entails the effect + */ + public boolean query(CausalKnowledgeBase cbase, PlFormula observation, PlFormula effect) { + Collection observations = new HashSet<>(); + observations.add(observation); + return query(cbase, observations, effect); + } + + @Override + public Boolean query(CausalKnowledgeBase cbase, PlFormula effect) { + return query(cbase, new HashSet<>(), effect); + } + + /** + * Determines whether the given causal statements holds under the causal knowledge base + * + * @param cbase some causal knowledge base + * @param statement some causal statement + * @return TRUE iff the causal statement holds under the causal knowledge base + */ + public boolean query(CausalKnowledgeBase cbase, CausalStatement statement) { + return query(cbase, statement.getObservations(), statement.getConclusion()); + } + + /** + * + * @param cbase + * @param observations + * @return + */ + public abstract Collection getConclusions(CausalKnowledgeBase cbase, Collection observations); + + /** + * + * @param cbase + * @param observation + * @return + */ + public Collection getConclusions(CausalKnowledgeBase cbase, PlFormula observation) { + Collection observations = new HashSet<>(); + observations.add(observation); + return getConclusions(cbase, observations); + } + + /** + * + * @param cbase + * @return + */ + public Collection getConclusions(CausalKnowledgeBase cbase) { + return getConclusions(cbase, new HashSet<>()); + } + + @Override + public boolean isInstalled() { + return true; + } +} diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java index 614c08125..c386765f6 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java @@ -18,35 +18,144 @@ */ package org.tweetyproject.causal.reasoner; -import org.tweetyproject.arg.deductive.syntax.DeductiveKnowledgeBase; -import org.tweetyproject.causal.semantics.CausalStatement; -import org.tweetyproject.causal.syntax.InducedTheory; -import org.tweetyproject.logics.pl.syntax.PlFormula; +import org.tweetyproject.arg.dung.reasoner.AbstractExtensionReasoner; +import org.tweetyproject.arg.dung.reasoner.SimpleStableReasoner; +import org.tweetyproject.arg.dung.semantics.Extension; +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.syntax.CausalArgument; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.commons.util.SetTools; +import org.tweetyproject.logics.pl.reasoner.AbstractPlReasoner; +import org.tweetyproject.logics.pl.reasoner.SimplePlReasoner; +import org.tweetyproject.logics.pl.syntax.*; import java.util.Collection; +import java.util.HashSet; -public class ArgumentationBasedCausalReasoner { +/** + * Implements the approach to argumentation-based causal reasoning as described in
+ *
+ * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, + * 'Argumentation-based Causal and Counterfactual Reasoning', + * 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), (2022) + * + * + * @author Lars Bengel + */ +public class ArgumentationBasedCausalReasoner extends AbstractCausalReasoner { + /** Internal reasoner */ + protected final AbstractPlReasoner reasoner = new SimplePlReasoner(); + protected final AbstractExtensionReasoner stableReasoner = new SimpleStableReasoner(); - public InducedTheory getInducedTheory(Collection observations) { + /** + * Constructs a logical argumentation framework from a given causal knowledge base and some observations + * + * @param cbase some causal knowledge base + * @param observations some logical formulae representing the observations of causal atoms + * @return the argumentation framework induced from the causal knowledge base and the observations + */ + public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations) { + PlBeliefSet base = new PlBeliefSet(cbase.getBeliefs()); + base.addAll(observations); - DeductiveKnowledgeBase knowledgeBase = new DeductiveKnowledgeBase(); - // TODO incorporate observations into argument construction - // Utilise DeductiveArgumentation package ?? - return null; - } - public boolean query(CausalStatement statement) { - return true; - } + Collection literals = new HashSet<>(); + for (Proposition atom : base.getSignature()) { + literals.add(atom); + literals.add(new Negation(atom)); + } + // Construct all possible arguments + // TODO enhance arguments by adding all applied rules (see AbstractPlReasoner.getKernels()) + DungTheory theory = new DungTheory(); + for (PlFormula literal : literals) { + if (reasoner.query(base, literal)) { + // follows from observations directly + CausalArgument argument = new CausalArgument(new HashSet<>(), literal); + //System.out.printf("%s: %s%n",literal,reasoner.getKernels(base, literal)); + theory.add(argument); + continue; + } + // Compute all sets of assumptions that entail the atom + // TODO optimise using {@link SubsetIterator} + Collection> candidates = new HashSet<>(); + for (Collection assumptions : new SetTools().subsets(cbase.getAssumptions())) { + if (reasoner.query(new PlBeliefSet(assumptions), new Contradiction())) continue; + PlBeliefSet knowledge = new PlBeliefSet(base); + knowledge.addAll(assumptions); + if (reasoner.query(knowledge, new Contradiction())) continue; + if (reasoner.query(knowledge, literal)) { + candidates.add(assumptions); + } + } - public boolean query(Collection observations, PlFormula effect) { - return true; + // Find the minimal sets + for (Collection a1 : candidates) { + boolean isMinimal = true; + for (Collection a2: candidates) { + if (a1.equals(a2)) continue; + if (a1.containsAll(a2)) { + isMinimal = false; + break; + } + } + if (isMinimal) { + CausalArgument argument = new CausalArgument(a1, literal); + theory.add(argument); + } + } + } + + // Construct undercut attacks + for (Argument arg1 : theory) { + for (Argument arg2 : theory) { + for(PlFormula premise : ((CausalArgument) arg2).getPremises()) { + if(((CausalArgument) arg1).getConclusion().complement().equals(premise)) { + theory.addAttack(arg1, arg2); + break; + } + } + } + } + return theory; } - public Collection getConclusions(PlFormula observation) { - return null; + @Override + public boolean query(CausalKnowledgeBase cbase, Collection observations, PlFormula effect) { + DungTheory theory = getInducedTheory(cbase, observations); + Collection> extensions = stableReasoner.getModels(theory); + + //System.out.println(extensions); + for (Extension extension : extensions) { + boolean concludesEffect = false; + for (Argument argument : extension) { + if (((CausalArgument) argument).getConclusion().equals(effect)) { + concludesEffect = true; + break; + } + } + if (!concludesEffect) { + return false; + } + } + return true; } - public Collection getConclusions(Collection observations) { - return null; + @Override + public Collection getConclusions(CausalKnowledgeBase cbase, Collection observations) { + Collection result = new HashSet<>(); + for (Proposition prop : cbase.getSignature()) { + result.add(prop); + result.add(new Negation(prop)); + } + DungTheory theory = getInducedTheory(cbase, observations); + Collection> extensions = stableReasoner.getModels(theory); + for (Extension extension : extensions) { + Collection conclusions = new HashSet<>(); + for (Argument argument : extension) { + conclusions.add(((CausalArgument) argument).getConclusion()); + } + result.retainAll(conclusions); + } + return result; } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/CausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/CausalReasoner.java deleted file mode 100644 index fbc408cba..000000000 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/CausalReasoner.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of "TweetyProject", a collection of Java libraries for - * logical aspects of artificial intelligence and knowledge representation. - * - * TweetyProject is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * Copyright 2024 The TweetyProject Team - */ -package org.tweetyproject.causal.reasoner; - -import org.tweetyproject.logics.pl.syntax.PlFormula; - -import java.util.Collection; - -public abstract class CausalReasoner { - - protected Collection observations; - - /* - remove observations from CKB - CKB is a BeliefSet for the struct equations + a set of assumptions - observations only in the causal reasoner - causal reasoner has method to create AF, get conclusions, verify conclusions - */ -} diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java index b6d3159aa..0e02b8684 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java @@ -19,8 +19,6 @@ package org.tweetyproject.causal.semantics; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; -import org.tweetyproject.causal.syntax.InducedTheory; -import org.tweetyproject.arg.dung.util.DungTheoryPlotter; import org.tweetyproject.logics.pl.syntax.PlFormula; import java.util.Collection; @@ -34,7 +32,7 @@ * @author Lars Bengel */ public class CausalStatement { - private Collection conclusions; + private PlFormula conclusion; private Collection observations; @@ -44,9 +42,9 @@ public class CausalStatement { * @param conclusions conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. * @param premises observations of the causal atoms */ - public CausalStatement(Collection conclusions, Collection premises) { + public CausalStatement(PlFormula conclusion, Collection premises) { super(); - this.conclusions = conclusions; + this.conclusion = conclusion; this.observations = premises; } @@ -54,8 +52,8 @@ public CausalStatement(Collection conclusions, Collection * Retrieves the conclusions of this causal statement. * @return A new HashSet containing all the conclusions of this causal statement. */ - public Collection getConclusions(){ - return new HashSet<>(this.conclusions); + public PlFormula getConclusion(){ + return this.conclusion; } /** @@ -71,6 +69,7 @@ public Collection getObservations(){ * @param ckbase Causal knowledge base * @return TRUE iff this instance holds in the specified knowledge base. */ + /* public boolean holds(CausalKnowledgeBase ckbase) { for(var conclusion : this.getConclusions()) { if(!ckbase.entails(this.getObservations(), conclusion)) { @@ -80,6 +79,8 @@ public boolean holds(CausalKnowledgeBase ckbase) { return true; } + + */ /** * Visualizes this causal statement within a given causal knowledge base. This method generates a visual representation @@ -88,11 +89,14 @@ public boolean holds(CausalKnowledgeBase ckbase) { * * @param cKbase The causal knowledge base used for visualization. */ + /* public void VisualizeHolds(CausalKnowledgeBase cKbase) { var causalKnowledgeBaseCopy = cKbase.clone(); causalKnowledgeBaseCopy.addAll(this.getObservations()); var inducedAF = new InducedTheory(causalKnowledgeBaseCopy); - DungTheoryPlotter.plotFramework(inducedAF, 3000, 2000, "Premises: " + this.getObservations().toString() + " \n Conclusions: " + this.getConclusions().toString()); + DungTheoryPlotter.plotFramework(inducedAF, 3000, 2000, "Premises: " + this.getObservations().toString() + " \n Conclusions: " + this.getConclusion().toString()); } + + */ } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java index 17a988a27..baaaae2bf 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java @@ -43,10 +43,11 @@ public class CounterfactualStatement extends InterventionalStatement { * @param interventions Maps explainable atoms to boolean values. * @param premises PlFormulas which have to be true, so that the conclusions can be drawn. */ - public CounterfactualStatement(Collection conclusions, Map interventions, Collection premises) { - super(conclusions, interventions, premises); + public CounterfactualStatement(PlFormula conclusion, Map interventions, Collection premises) { + super(conclusion, interventions, premises); } + /* @Override public boolean holds(CausalKnowledgeBase ckbase) { for(PlFormula conclusion : this.getConclusions()) { @@ -56,6 +57,8 @@ public boolean holds(CausalKnowledgeBase ckbase) { } return true; } + + */ /*@Override public void VisualizeHolds(CausalKnowledgeBase cKbase) { @@ -67,7 +70,8 @@ public void VisualizeHolds(CausalKnowledgeBase cKbase) { + " \n Interventions: " + this.getInterventions().toString() + " \n Conclusions: " + this.getConclusions().toString()); }*/ - + + /* private boolean checkCounterFactualStatement(CausalKnowledgeBase ckbase, PlFormula conclusion) { StructuralCausalModel twinModel = ckbase.getCausalModel().getTwinModel(); for (Proposition atom : this.interventions.keySet()) { @@ -75,4 +79,6 @@ private boolean checkCounterFactualStatement(CausalKnowledgeBase ckbase, PlFormu } return new CausalKnowledgeBase(twinModel, ckbase.getAssumptions()).entails(this.getObservations(), conclusion); } + + */ } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java index ee70d26a4..6140300a8 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java @@ -46,15 +46,17 @@ public class InterventionalStatement extends CausalStatement { * @param interventions Maps explainable atoms to boolean values. * @param observations PlFormulas which have to be true, so that the conclusions can be drawn. */ - public InterventionalStatement(Collection conclusions, Map interventions, Collection observations) { - super(conclusions, observations); + public InterventionalStatement(PlFormula conclusion, Map interventions, Collection observations) { + super(conclusion, observations); this.interventions = interventions; } - + + /** * Retrieves the interventions of this causal statement. * @return A HashMap containing the interventions mapped from explainable atoms to their respective boolean values. */ + /* public Map getInterventions(){ return new HashMap<>(this.interventions); } @@ -69,6 +71,7 @@ public boolean holds(CausalKnowledgeBase ckbase) { return true; } + */ /*@Override public void VisualizeHolds(CausalKnowledgeBase cKbase) @@ -81,7 +84,7 @@ public void VisualizeHolds(CausalKnowledgeBase cKbase) + " \n Interventions: " + this.getInterventions().toString() + " \n Conclusions: " + this.getConclusions().toString()); }*/ - + /* private boolean checkInterventionalStatement(CausalKnowledgeBase ckbase, PlFormula conclusion) { CausalKnowledgeBase newBase = ckbase.clone(); for (Proposition atom : interventions.keySet()) { @@ -89,4 +92,6 @@ private boolean checkInterventionalStatement(CausalKnowledgeBase ckbase, PlFormu } return newBase.entails(this.getObservations(), conclusion); } + + */ } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedArgument.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalArgument.java similarity index 91% rename from org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedArgument.java rename to org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalArgument.java index 615adb17f..9731a60b9 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedArgument.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalArgument.java @@ -31,7 +31,7 @@ * @author Julian Sander * @author Lars Bengel */ -public class InducedArgument extends Argument { +public class CausalArgument extends Argument { /** Premises of the argument */ private final Collection premises; /** Conclusion of the argument */ @@ -42,8 +42,8 @@ public class InducedArgument extends Argument { * @param premises the set of premises of the argument * @param conclusion the conclusion of the argument */ - public InducedArgument(Collection premises, PlFormula conclusion) { - super(String.format("(%s, %s)", premises.toString(), conclusion.toString())); + public CausalArgument(Collection premises, PlFormula conclusion) { + super(String.format("(%s -> %s)", premises.toString(), conclusion.toString())); this.premises = new HashSet<>(premises); this.conclusion = conclusion; diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java index 022f8b945..21000c2ef 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java @@ -47,6 +47,7 @@ public class CausalKnowledgeBase extends PlBeliefSet { * @param model some causal model */ public CausalKnowledgeBase(StructuralCausalModel model) { + super(model); this.model = model.clone(); this.assumptions = new HashSet<>(); } @@ -93,9 +94,10 @@ public boolean addAssumption(PlFormula assumption) { * Removes an assumption from this knowledge base. * * @param assumption The assumption to be removed. - * @return true if the assumption was successfully removed, false if it was not found in the set. + * @return true if the assumption was successfully removed, false otherwise. */ public boolean removeAssumption(PlFormula assumption) { + // TODO cannot remove the only assumption for a background atom return this.assumptions.remove(assumption); } @@ -122,43 +124,6 @@ public Collection getBeliefs() { return result; } - /** - * Determines whether the specified conclusion can be inferred from the given premises via this knowledge base. - * - * @param premises Set of formulas, which will be added to this knowledge base - * @param conclusion Formula, which is checked to be a conclusion of the combination of this instance and the specified premises or not - * @return "True" iff the specified formula is a conclusion of this knowledge base and the specified set of premises. - */ - public boolean entails(Collection premises, PlFormula conclusion) { - Collection beliefs = this.getBeliefs(); - beliefs.addAll(premises); - PlBeliefSet beliefSet = new PlBeliefSet(beliefs); - return new SimplePlReasoner().query(beliefSet, conclusion); - } - - /** - * Returns all 1-atom-conclusions of this instance if the specified set of formulas is - * used as premises. - * @param premises Set of formulas which are added to this knowledge base to get to the returned conclusions. - * @return Set of formulas, that can be concluded from this knowledge base, if the specified formulas are added. - */ - public Collection getSingleAtomConclusions(Collection premises) { - Collection conclusions = new HashSet<>(); - for(PlFormula formula : this.model.getStructuralEquations()) { - for(Proposition atom : formula.getAtoms()) { - if (this.entails(premises, atom)) { - conclusions.add(atom); - } else { - PlFormula negAtom = new Negation(atom); - if(this.entails(premises, negAtom)){ - conclusions.add(negAtom); - } - } - } - } - return conclusions; - } - @Override public String toString() { return String.format("(%s, %s)", getCausalModel(), getAssumptions()); diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedTheory.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedTheory.java deleted file mode 100644 index 6caac6993..000000000 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/InducedTheory.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * This file is part of "TweetyProject", a collection of Java libraries for - * logical aspects of artificial intelligence and knowledge representation. - * - * TweetyProject is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * Copyright 2024 The TweetyProject Team - */ -package org.tweetyproject.causal.syntax; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import org.tweetyproject.arg.dung.reasoner.AbstractExtensionReasoner; -import org.tweetyproject.arg.dung.semantics.Extension; -import org.tweetyproject.arg.dung.semantics.Semantics; -import org.tweetyproject.arg.dung.syntax.Argument; -import org.tweetyproject.arg.dung.syntax.DungTheory; -import org.tweetyproject.commons.util.SetTools; -import org.tweetyproject.logics.pl.syntax.PlFormula; - -/** - * This class describes an {@link DungTheory argumentation framework} that was induced by a {@link CausalKnowledgeBase} - * - * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, 'Argumentation-based Causal and Counterfactual Reasoning', 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), 2022" - * - * @see DungTheory - * - * @author Julian Sander - * @author Lars Bengel - */ -public class InducedTheory extends DungTheory { - - private CausalKnowledgeBase knowledgeBase; - - /** - * Induces an argumentation framework from a causal knowledge base - * @param knowledgeBase some causal knowledge base - */ - public InducedTheory(CausalKnowledgeBase knowledgeBase) { - this.knowledgeBase = knowledgeBase; - - // TODO fix argument construction (minimality, relevance?) - for(Set subSetAssumptions : new SetTools().subsets(knowledgeBase.getAssumptions())) { - for(PlFormula conclusion : knowledgeBase.getSingleAtomConclusions(subSetAssumptions)) { - this.add(new InducedArgument(subSetAssumptions, conclusion)); - } - } - - for(Argument arg1 : this.getNodes()) { - InducedArgument cArg1 = (InducedArgument) arg1; - for(Argument arg2 : this.getNodes()) { - InducedArgument cArg2 = (InducedArgument) arg2; - if(this.checkUndercut(cArg1, cArg2)) { - this.addAttack(cArg1, cArg2); - } - } - } - } - - @Override - public boolean add(Argument argument) { - if(argument instanceof InducedArgument) { - if (!this.knowledgeBase.getAssumptions().containsAll(((InducedArgument) argument).getPremises())) { - throw new IllegalArgumentException("Argument contains invalid assumptions"); - } else if (!this.knowledgeBase.entails(((InducedArgument) argument).getPremises(), ((InducedArgument) argument).getConclusion())) { - throw new IllegalArgumentException("Argument has invalid conclusion"); - } // TODO check minimality - - return super.add(argument); - } else { - throw new IllegalArgumentException("Argument has wrong type"); - } - } - - @Override - public boolean addAttack(Argument attacker, Argument attacked) { - if(!((attacker instanceof InducedArgument) && (attacked instanceof InducedArgument))) { - throw new IllegalArgumentException("Argument has wrong type"); - } - - return this.addAttack((InducedArgument) attacker, (InducedArgument) attacked); - } - - /** - * Adds an attack from the first argument to the second to this theory. - * - * @param attacker Argument which undercuts the second argument. - * @param attacked Argument which is undercut by the first argument. - * @return TRUE iff the set of attacks was changed - */ - public boolean addAttack(InducedArgument attacker, InducedArgument attacked) { - if(!this.checkUndercut(attacker, attacked)) { - throw new IllegalArgumentException("The attacking argument does not undercut the attacked argument"); - } - - return super.addAttack(attacker, attacked); - } - - /** - * This method checks if a specified formula can be concluded from this instance, - * by checking whether all stable extensions contain at least one argument with the given conclusion. - * - * @param conclusion some formula, which is in question to be a conclusion of this instance. - * @return TRUE iff the conclusion can be drawn from this instance. FALSE if not. - */ - public boolean entails(PlFormula conclusion) { - AbstractExtensionReasoner reasoner = AbstractExtensionReasoner.getSimpleReasonerForSemantics(Semantics.ST); - Collection> extensions = reasoner.getModels(this); - - boolean allExtContainConclusion = true; - for(Extension ext : extensions) { - boolean hasConclusion = false; - for(Argument argument : ext) { - if(((InducedArgument) argument).getConclusion().equals(conclusion)) { - hasConclusion = true; - break; - } - } - if(!hasConclusion) { - allExtContainConclusion = false; - break; - } - } - return allExtContainConclusion; - } - - /** - * Retrieves all InducedArguments from the theory. - * - * @return A set of all InducedArguments within this theory. - */ - public Collection getArguments(){ - var output = new HashSet(); - for(var argument : this.getNodes()) { - output.add((InducedArgument) argument); - } - return output; - } - - /** - * Retrieves the causal knowledge base associated with this theory. - * - * @return The causal knowledge base from which this theory is induced. - */ - public CausalKnowledgeBase getKnowledgeBase() { - return this.knowledgeBase; - } - - /** - * Checks if one argument undercuts another based on the conclusions and premises. - * - * @param attacker The argument that potentially undercuts. - * @param victim The argument that is potentially undercut. - * @return true iff the attacker undercuts the victim, otherwise false. - */ - private boolean checkUndercut(InducedArgument attacker, InducedArgument victim) { - for(PlFormula premise : victim.getPremises()) { - if(attacker.getConclusion().complement().equals(premise)) { - return true; - } - } - - return false; - } -} From e2b48979877a80aaedcc5121a05ebcef5d19ff06 Mon Sep 17 00:00:00 2001 From: Lars Bengel Date: Wed, 16 Oct 2024 14:21:59 +0200 Subject: [PATCH 11/43] updated example --- ....java => CausalReasoningExampleVirus.java} | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) rename org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/{CausalReasoningExample1.java => CausalReasoningExampleVirus.java} (50%) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java similarity index 50% rename from org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java rename to org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java index 64b4e0cdf..ba6f6a760 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExample1.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java @@ -1,5 +1,7 @@ package org.tweetyproject.causal.examples; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.reasoner.ArgumentationBasedCausalReasoner; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.causal.syntax.StructuralCausalModel; import org.tweetyproject.logics.pl.syntax.*; @@ -7,7 +9,14 @@ import java.util.Collection; import java.util.HashSet; -public class CausalReasoningExample1 { +/** + * + */ +public class CausalReasoningExampleVirus { + /** + * + * @param args cmdline arguments (unused) + */ public static void main(String[] args) throws StructuralCausalModel.CyclicDependencyException { // Background atoms Proposition corona = new Proposition("corona"); @@ -35,14 +44,30 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend model.addExplainableAtom(shortOfBreath, new Conjunction(covid, atRisk)); // Initialize causal knowledge base - CausalKnowledgeBase kbase = new CausalKnowledgeBase(model); + CausalKnowledgeBase cbase = new CausalKnowledgeBase(model); // Add assumptions - kbase.addAssumption(new Negation(corona)); - kbase.addAssumption(new Negation(influenza)); - kbase.addAssumption(new Negation(atRisk)); - kbase.addAssumption(atRisk); + cbase.addAssumption(new Negation(corona)); + cbase.addAssumption(new Negation(influenza)); + cbase.addAssumption(new Negation(atRisk)); + cbase.addAssumption(atRisk); + //cbase.addAssumption(influenza); - System.out.println(kbase); - // TODO + System.out.println("Causal Knowledge Base: " + cbase); + + Collection observations = new HashSet<>(); + observations.add(fever); + + // Initialize Causal Reasoner and induce an argumentation framework + ArgumentationBasedCausalReasoner reasoner = new ArgumentationBasedCausalReasoner(); + DungTheory theory = reasoner.getInducedTheory(cbase, observations); + + System.out.println("Induced Argumentation Framework:"); + System.out.println(theory.prettyPrint()); + + // Do some causal reasoning + System.out.println("Observing 'fever' implies 'shortOfBreath': " + reasoner.query(cbase, observations, shortOfBreath)); + System.out.println("Observing 'fever' implies 'not shortOfBreath': " + reasoner.query(cbase, observations, new Negation(shortOfBreath))); + + System.out.printf("Possible Conclusions of observing '%2$s': %1$s", reasoner.getConclusions(cbase, observations), observations); } } From b932f6a28cdb110626e4b5fa5952b3b7486f635d Mon Sep 17 00:00:00 2001 From: Lars Bengel Date: Wed, 16 Oct 2024 14:22:07 +0200 Subject: [PATCH 12/43] added new example --- .../CausalReasoningExampleSurfer.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java new file mode 100644 index 000000000..7c9f7f33f --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java @@ -0,0 +1,70 @@ +package org.tweetyproject.causal.examples; + +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.reasoner.ArgumentationBasedCausalReasoner; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.causal.syntax.StructuralCausalModel; +import org.tweetyproject.logics.pl.syntax.*; + +import java.util.Collection; +import java.util.HashSet; + +/** + * @author Lars Bengel + */ +public class CausalReasoningExampleSurfer { + /** + * + * @param args cmdline arguments (unused) + */ + public static void main(String[] args) throws StructuralCausalModel.CyclicDependencyException { + // Background atoms + Proposition giantWave = new Proposition("giant-wave"); + Proposition strongCurrent = new Proposition("strong-current"); + Proposition jellyfish = new Proposition("jellyfish"); + + // Explainable atoms + Proposition brokenBoard = new Proposition("broken-board"); + Proposition submersion = new Proposition("submersion"); + Proposition cramp = new Proposition("cramp"); + Proposition drowning = new Proposition("drowning"); + + // Construct Causal Model + StructuralCausalModel model = new StructuralCausalModel(); + // Add background atoms + model.addBackgroundAtom(giantWave); + model.addBackgroundAtom(strongCurrent); + model.addBackgroundAtom(jellyfish); + // Add structural equations + model.addExplainableAtom(brokenBoard, giantWave); + model.addExplainableAtom(submersion, new Conjunction(giantWave, strongCurrent)); + model.addExplainableAtom(cramp, new Disjunction(strongCurrent, jellyfish)); + model.addExplainableAtom(drowning, new Disjunction(cramp, submersion)); + + // Initialize causal knowledge base + CausalKnowledgeBase cbase = new CausalKnowledgeBase(model); + // Add assumptions + cbase.addAssumption(new Negation(strongCurrent)); + cbase.addAssumption(strongCurrent); + cbase.addAssumption(giantWave); + cbase.addAssumption(jellyfish); + + System.out.println("Causal Knowledge Base: " + cbase); + + Collection observations = new HashSet<>(); + observations.add(drowning); + + // Initialize Causal Reasoner and induce an argumentation framework + ArgumentationBasedCausalReasoner reasoner = new ArgumentationBasedCausalReasoner(); + DungTheory theory = reasoner.getInducedTheory(cbase, observations); + + System.out.println("Induced Argumentation Framework:"); + System.out.println(theory.prettyPrint()); + + // Do some causal reasoning + System.out.println("Observing 'drowning' implies 'submersion': " + reasoner.query(cbase, observations, cramp)); + System.out.println("Observing 'drowning' implies 'not submersion': " + reasoner.query(cbase, observations, new Negation(submersion))); + + System.out.printf("Possible Conclusions of observing '%2$s': %1$s", reasoner.getConclusions(cbase, observations), observations); + } +} From 501db7c8b8d7b66c73158ddfabf94f756787d577 Mon Sep 17 00:00:00 2001 From: Lars Bengel Date: Wed, 16 Oct 2024 14:33:33 +0200 Subject: [PATCH 13/43] added new interface to causalReasoner --- .../reasoner/AbstractCausalReasoner.java | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java index 0e315ed4e..4721a6c85 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java @@ -18,22 +18,25 @@ */ package org.tweetyproject.causal.reasoner; +import org.tweetyproject.arg.dung.semantics.Extension; import org.tweetyproject.causal.semantics.CausalStatement; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.commons.Interpretation; +import org.tweetyproject.commons.ModelProvider; import org.tweetyproject.commons.QualitativeReasoner; import org.tweetyproject.logics.pl.syntax.PlBeliefSet; import org.tweetyproject.logics.pl.syntax.PlFormula; import java.util.Collection; import java.util.HashSet; +import java.util.List; /** * General abstract reasoner for basic causal reasoning with {@link CausalKnowledgeBase} * * @author Lars Bengel */ -public abstract class AbstractCausalReasoner implements QualitativeReasoner { - +public abstract class AbstractCausalReasoner implements QualitativeReasoner, ModelProvider> { /** * Determines whether the given effect is entailed by the causal knowledge base together with the observations * @@ -48,7 +51,7 @@ public abstract class AbstractCausalReasoner implements QualitativeReasoner(), effect); - } - /** * Determines whether the given causal statements holds under the causal knowledge base * @@ -74,19 +72,26 @@ public boolean query(CausalKnowledgeBase cbase, CausalStatement statement) { return query(cbase, statement.getObservations(), statement.getConclusion()); } + @Override + public Boolean query(CausalKnowledgeBase cbase, PlFormula effect) { + return query(cbase, new HashSet<>(), effect); + } + /** + * Computes the set of all literal expressions that can be concluded from the causal knowledge base and observations * - * @param cbase - * @param observations - * @return + * @param cbase some causal knowledge base + * @param observations some logical formulae over atoms of the causal knowledge base + * @return the set of expressions that can be concluded from the given knowledge */ public abstract Collection getConclusions(CausalKnowledgeBase cbase, Collection observations); /** + * Computes the set of all literal expressions that can be concluded from the causal knowledge base and observation * - * @param cbase - * @param observation - * @return + * @param cbase some causal knowledge base + * @param observation some logical formula over atoms of the causal knowledge base + * @return the set of expressions that can be concluded from the given knowledge */ public Collection getConclusions(CausalKnowledgeBase cbase, PlFormula observation) { Collection observations = new HashSet<>(); @@ -95,14 +100,27 @@ public Collection getConclusions(CausalKnowledgeBase cbase, PlFormula } /** + * Computes the set of all literal expressions that can be concluded from the causal knowledge base * - * @param cbase - * @return + * @param cbase some causal knowledge base + * @return the set of expressions that can be concluded from the given knowledge */ public Collection getConclusions(CausalKnowledgeBase cbase) { return getConclusions(cbase, new HashSet<>()); } + @Override + public Collection> getModels(CausalKnowledgeBase bbase) { + // TODO implement + return List.of(); + } + + @Override + public Interpretation getModel(CausalKnowledgeBase bbase) { + // TODO implement + return null; + } + @Override public boolean isInstalled() { return true; From 4081e713cef90e339fda9a5a2f85edd7d7927023 Mon Sep 17 00:00:00 2001 From: Lars Bengel Date: Wed, 16 Oct 2024 14:42:24 +0200 Subject: [PATCH 14/43] minor fixes --- .../examples/CausalReasoningExampleVirus.java | 5 +++ .../causal/semantics/CausalStatement.java | 38 +------------------ .../semantics/CounterfactualStatement.java | 26 +------------ .../semantics/InterventionalStatement.java | 30 ++------------- .../causal/syntax/CausalArgument.java | 2 +- 5 files changed, 11 insertions(+), 90 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java index ba6f6a760..777dc879a 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java @@ -1,6 +1,7 @@ package org.tweetyproject.causal.examples; import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.arg.dung.util.DungTheoryPlotter; import org.tweetyproject.causal.reasoner.ArgumentationBasedCausalReasoner; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.causal.syntax.StructuralCausalModel; @@ -69,5 +70,9 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend System.out.println("Observing 'fever' implies 'not shortOfBreath': " + reasoner.query(cbase, observations, new Negation(shortOfBreath))); System.out.printf("Possible Conclusions of observing '%2$s': %1$s", reasoner.getConclusions(cbase, observations), observations); + + + // Visualisation of the induced argumentation framework + //DungTheoryPlotter.plotFramework(theory, 3000, 2000, "Premises: " + observations + " \n Conclusion: " + shortOfBreath); } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java index 0e02b8684..93859d3e4 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java @@ -39,7 +39,7 @@ public class CausalStatement { /** * Initializes a new causal statement. * - * @param conclusions conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. + * @param conclusion conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. * @param premises observations of the causal atoms */ public CausalStatement(PlFormula conclusion, Collection premises) { @@ -62,41 +62,5 @@ public PlFormula getConclusion(){ */ public Collection getObservations(){ return new HashSet<>(this.observations); - } - - /** - * Checks if this instance holds in the specified knowledge base. - * @param ckbase Causal knowledge base - * @return TRUE iff this instance holds in the specified knowledge base. - */ - /* - public boolean holds(CausalKnowledgeBase ckbase) { - for(var conclusion : this.getConclusions()) { - if(!ckbase.entails(this.getObservations(), conclusion)) { - return false; - } - } - - return true; } - - */ - - /** - * Visualizes this causal statement within a given causal knowledge base. This method generates a visual representation - * of the argument framework induced by adding the premises of this statement to the causal knowledge base, highlighting - * the conclusions. - * - * @param cKbase The causal knowledge base used for visualization. - */ - /* - public void VisualizeHolds(CausalKnowledgeBase cKbase) - { - var causalKnowledgeBaseCopy = cKbase.clone(); - causalKnowledgeBaseCopy.addAll(this.getObservations()); - var inducedAF = new InducedTheory(causalKnowledgeBaseCopy); - DungTheoryPlotter.plotFramework(inducedAF, 3000, 2000, "Premises: " + this.getObservations().toString() + " \n Conclusions: " + this.getConclusion().toString()); - } - - */ } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java index baaaae2bf..fde9dfcb6 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java @@ -39,7 +39,7 @@ public class CounterfactualStatement extends InterventionalStatement { /** * Initializes a new counterfactual causal statement. - * @param conclusions Conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. + * @param conclusion Conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. * @param interventions Maps explainable atoms to boolean values. * @param premises PlFormulas which have to be true, so that the conclusions can be drawn. */ @@ -47,30 +47,6 @@ public CounterfactualStatement(PlFormula conclusion, Map i super(conclusion, interventions, premises); } - /* - @Override - public boolean holds(CausalKnowledgeBase ckbase) { - for(PlFormula conclusion : this.getConclusions()) { - if(!checkCounterFactualStatement(ckbase, conclusion)) { - return false; - } - } - return true; - } - - */ - - /*@Override - public void VisualizeHolds(CausalKnowledgeBase cKbase) { - var causalKnowledgeBaseCopy = getIntervenedTwinModel(cKbase); - causalKnowledgeBaseCopy.addAll(this.getObservations()); - var inducedAF = new InducedTheory(causalKnowledgeBaseCopy); - DungTheoryPlotter.plotFramework(inducedAF, 3000, 2000, - "Premises: " + this.getObservations().toString() - + " \n Interventions: " + this.getInterventions().toString() - + " \n Conclusions: " + this.getConclusions().toString()); - }*/ - /* private boolean checkCounterFactualStatement(CausalKnowledgeBase ckbase, PlFormula conclusion) { StructuralCausalModel twinModel = ckbase.getCausalModel().getTwinModel(); diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java index 6140300a8..41d4b65b0 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java @@ -42,7 +42,7 @@ public class InterventionalStatement extends CausalStatement { /** * Initializes a new interventional causal statement. * - * @param conclusions Conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. + * @param conclusion Conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. * @param interventions Maps explainable atoms to boolean values. * @param observations PlFormulas which have to be true, so that the conclusions can be drawn. */ @@ -51,39 +51,15 @@ public InterventionalStatement(PlFormula conclusion, Map i this.interventions = interventions; } - /** * Retrieves the interventions of this causal statement. - * @return A HashMap containing the interventions mapped from explainable atoms to their respective boolean values. + * @return A Msp containing the interventions mapped from explainable atoms to their respective boolean values. */ - /* + public Map getInterventions(){ return new HashMap<>(this.interventions); } - @Override - public boolean holds(CausalKnowledgeBase ckbase) { - for(var conclusion : this.getConclusions()) { - if(!checkInterventionalStatement(ckbase, conclusion)) { - return false; - } - } - - return true; - } - */ - - /*@Override - public void VisualizeHolds(CausalKnowledgeBase cKbase) - { - var causalKnowledgeBaseCopy = getIntervenedCopy(cKbase); - causalKnowledgeBaseCopy.addAll(this.getObservations()); - var inducedAF = new InducedTheory(causalKnowledgeBaseCopy); - DungTheoryPlotter.plotFramework(inducedAF, 3000, 2000, - "Premises: " + this.getObservations().toString() - + " \n Interventions: " + this.getInterventions().toString() - + " \n Conclusions: " + this.getConclusions().toString()); - }*/ /* private boolean checkInterventionalStatement(CausalKnowledgeBase ckbase, PlFormula conclusion) { CausalKnowledgeBase newBase = ckbase.clone(); diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalArgument.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalArgument.java index 9731a60b9..230791ed9 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalArgument.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalArgument.java @@ -26,7 +26,7 @@ /** * This class represents a logical {@link Argument} induced from a {@link CausalKnowledgeBase} - * An Argument consists of a set of assumptions (premises) and a conclusion + * Such an Argument consists of a set of assumptions (premises) and a causal conclusion * * @author Julian Sander * @author Lars Bengel From b0b787cb207462ea54d9a8fc2aef8ea26b861783 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Wed, 16 Oct 2024 22:04:51 +0200 Subject: [PATCH 15/43] refactored causal statements --- .../reasoner/AbstractCausalReasoner.java | 2 +- .../causal/semantics/CausalStatement.java | 48 ++++++++++++++----- .../semantics/CounterfactualStatement.java | 42 +++++++++++++--- .../semantics/InterventionalStatement.java | 47 ++++++++++++++---- .../causal/syntax/StructuralCausalModel.java | 15 +++++- 5 files changed, 124 insertions(+), 30 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java index 4721a6c85..4095736f1 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java @@ -90,7 +90,7 @@ public Boolean query(CausalKnowledgeBase cbase, PlFormula effect) { * Computes the set of all literal expressions that can be concluded from the causal knowledge base and observation * * @param cbase some causal knowledge base - * @param observation some logical formula over atoms of the causal knowledge base + * @param observation some logical formula over atoms of the causal knowledge base * @return the set of expressions that can be concluded from the given knowledge */ public Collection getConclusions(CausalKnowledgeBase cbase, PlFormula observation) { diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java index 93859d3e4..386fb8981 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalStatement.java @@ -32,33 +32,59 @@ * @author Lars Bengel */ public class CausalStatement { + /** Conclusion or effect of the causal statement */ private PlFormula conclusion; - + /** Observations or premises of the causal statement */ private Collection observations; /** - * Initializes a new causal statement. + * Initializes a new empty causal statement + */ + public CausalStatement() { + this.observations = new HashSet<>(); + this.conclusion = null; + } + + /** + * Initializes a new causal statement * - * @param conclusion conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. - * @param premises observations of the causal atoms + * @param observations observations of the causal atoms + * @param conclusion the conclusion of the causal statement + */ + public CausalStatement(Collection observations, PlFormula conclusion) { + this.conclusion = conclusion; + this.observations = new HashSet<>(observations); + } + + /** + * Add a new observation to the causal statement + * + * @param observation some observation of a causal atom + * @return TRUE iff observation is added successfully + */ + public boolean addObservation(PlFormula observation) { + return this.observations.add(observation); + } + + /** + * Set a new conclusion of the causal statement + * @param conclusion the new conclusion */ - public CausalStatement(PlFormula conclusion, Collection premises) { - super(); + public void setConclusion(PlFormula conclusion) { this.conclusion = conclusion; - this.observations = premises; } /** - * Retrieves the conclusions of this causal statement. - * @return A new HashSet containing all the conclusions of this causal statement. + * Retrieves the conclusion of this causal statement + * @return The conclusion of this causal statement. */ public PlFormula getConclusion(){ return this.conclusion; } /** - * Retrieves the observations of this causal statement. - * @return A new HashSet containing all the premises required for the conclusions to hold. + * Retrieves the observations of this causal statement + * @return A copy of the observations of this causal statement */ public Collection getObservations(){ return new HashSet<>(this.observations); diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java index fde9dfcb6..1e4495edf 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java @@ -19,7 +19,6 @@ package org.tweetyproject.causal.semantics; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; -import org.tweetyproject.causal.syntax.StructuralCausalModel; import org.tweetyproject.logics.pl.syntax.PlFormula; import org.tweetyproject.logics.pl.syntax.Proposition; @@ -34,17 +33,46 @@ * @see "Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm 'Argumentation-based Causal and Counterfactual Reasoning' 1st International Workshop on Argumentation for eXplainable AI, 2022" * * @author Julian Sander + * @author Lars Bengel */ public class CounterfactualStatement extends InterventionalStatement { + /** + * Initializes a new counterfactual causal statement + * + * @param observations Observations of causal atoms + * @param interventions A set of interventions on causal atoms + * @param conclusion Conclusion of the causal statement + */ + public CounterfactualStatement(Collection observations, Map interventions, PlFormula conclusion) { + super(observations, interventions, conclusion); + } + + /** + * Initializes a new counterfactual causal statement without interventions + * + * @param observations Observations of causal atoms + * @param conclusion Conclusion of the causal statement + */ + public CounterfactualStatement(Collection observations, PlFormula conclusion) { + super(observations, conclusion); + } + + /** + * Initializes a new empty counterfactual causal statement + */ + public CounterfactualStatement() { + super(); + } /** - * Initializes a new counterfactual causal statement. - * @param conclusion Conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. - * @param interventions Maps explainable atoms to boolean values. - * @param premises PlFormulas which have to be true, so that the conclusions can be drawn. + * Add a new intervention which sets the counterfactual copy of the given atom to the given truth value + * @param atom some causal atom + * @param value some truth value + * @return TRUE iff the intervention is added successfully */ - public CounterfactualStatement(PlFormula conclusion, Map interventions, Collection premises) { - super(conclusion, interventions, premises); + public boolean addCounterfactualIntervention(Proposition atom, boolean value) { + Proposition cAtom = new Proposition(atom.getName()+"*"); + return Boolean.TRUE.equals(this.interventions.put(cAtom, value)); } /* diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java index 41d4b65b0..8506418da 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/InterventionalStatement.java @@ -37,25 +37,54 @@ * @author Lars Bengel */ public class InterventionalStatement extends CausalStatement { - + /** Interventions of this causal statement */ protected Map interventions; + + /** + * Initializes a new interventional causal statement + * + * @param observations Observations of causal atoms + * @param interventions A set of interventions on causal atoms + * @param conclusion Conclusion of the causal statement + */ + public InterventionalStatement(Collection observations, Map interventions, PlFormula conclusion) { + super(observations, conclusion); + this.interventions = new HashMap<>(interventions); + } + /** - * Initializes a new interventional causal statement. + * Initializes a new interventional causal statement without interventions * - * @param conclusion Conclusions, which would be true, iff this statement is true and the interventions were realized and the premises are met. - * @param interventions Maps explainable atoms to boolean values. - * @param observations PlFormulas which have to be true, so that the conclusions can be drawn. + * @param observations Observations of causal atoms + * @param conclusion Conclusion of the causal statement + */ + public InterventionalStatement(Collection observations, PlFormula conclusion) { + super(observations, conclusion); + this.interventions = new HashMap<>(); + } + + /** + * Initializes a new empty interventional causal statement + */ + public InterventionalStatement() { + super(); + this.interventions = new HashMap<>(); + } + + /** + * Add a new intervention which sets the given atom to the given truth value to the causal statement + * @param atom some causal atom + * @param value some truth value + * @return TRUE iff the intervention is added successfully */ - public InterventionalStatement(PlFormula conclusion, Map interventions, Collection observations) { - super(conclusion, observations); - this.interventions = interventions; + public boolean addIntervention(Proposition atom, boolean value) { + return Boolean.TRUE.equals(this.interventions.put(atom, value)); } /** * Retrieves the interventions of this causal statement. * @return A Msp containing the interventions mapped from explainable atoms to their respective boolean values. */ - public Map getInterventions(){ return new HashMap<>(this.interventions); } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java index c11cc29f2..2308bb0b2 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java @@ -232,6 +232,8 @@ public StructuralCausalModel getTwinModel() { * Removes the original cause of the atom and thus permanently changes the causal model * @param v some explainable atom * @param x Truth value of the intervention + * + * TODO should this be in-place instead? */ public StructuralCausalModel intervene(Proposition v, boolean x) { if(!this.explainableAtoms.containsKey(v)){ @@ -252,7 +254,7 @@ public String prettyPrint() { s.append("Background atoms: ").append(getBackgroundAtoms()).append("\n"); s.append("Structural Equations:\n"); for (Proposition atom : getExplainableAtoms()) { - s.append(String.format("%s<=> %s%n", atom, explainableAtoms.get(atom))); + s.append(String.format("%s <=> %s%n", atom, explainableAtoms.get(atom))); } return s.toString(); } @@ -354,8 +356,17 @@ public void clear() { throw new UnsupportedOperationException("Not Implemented"); } + /** + * Thrown to indicate that the structural equations of a causal model contain a cyclic dependency + */ public static class CyclicDependencyException extends Throwable { - public CyclicDependencyException(String string) { + /** + * Constructs a CyclicDependencyException with the specified detail message + * + * @param message the detail message + */ + public CyclicDependencyException(String message) { + super(message); } } } From f0cf75c6114b41634de142a1c7b9fd4fefbea48f Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Thu, 17 Oct 2024 12:30:14 +0200 Subject: [PATCH 16/43] added model representation for causal reasoning --- .../examples/CausalReasoningExampleVirus.java | 11 ++-- .../reasoner/AbstractCausalReasoner.java | 57 +++++++++++++++--- .../ArgumentationBasedCausalReasoner.java | 24 +++++++- ...umentationBasedCounterfactualReasoner.java | 60 +++++++++++++++++++ .../semantics/CausalInterpretation.java | 60 +++++++++++++++++++ 5 files changed, 197 insertions(+), 15 deletions(-) create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java index 777dc879a..c88f6cd75 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java @@ -56,21 +56,22 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend System.out.println("Causal Knowledge Base: " + cbase); Collection observations = new HashSet<>(); - observations.add(fever); + //observations.add(fever); // Initialize Causal Reasoner and induce an argumentation framework ArgumentationBasedCausalReasoner reasoner = new ArgumentationBasedCausalReasoner(); - DungTheory theory = reasoner.getInducedTheory(cbase, observations); + DungTheory theory = reasoner.getInducedTheory(cbase,observations); System.out.println("Induced Argumentation Framework:"); System.out.println(theory.prettyPrint()); // Do some causal reasoning - System.out.println("Observing 'fever' implies 'shortOfBreath': " + reasoner.query(cbase, observations, shortOfBreath)); - System.out.println("Observing 'fever' implies 'not shortOfBreath': " + reasoner.query(cbase, observations, new Negation(shortOfBreath))); + System.out.printf("Observing '%1$s' implies 'shortOfBreath': %2$s%n", observations, reasoner.query(cbase, observations, shortOfBreath)); + System.out.printf("Observing '%1$s' implies 'not shortOfBreath': %2$s%n", observations, reasoner.query(cbase, observations, new Negation(shortOfBreath))); - System.out.printf("Possible Conclusions of observing '%2$s': %1$s", reasoner.getConclusions(cbase, observations), observations); + System.out.printf("Possible Conclusions of observing '%2$s': %1$s%n", reasoner.getConclusions(cbase, observations), observations); + System.out.printf("Models: %s%n", reasoner.getModels(cbase, observations)); // Visualisation of the induced argumentation framework //DungTheoryPlotter.plotFramework(theory, 3000, 2000, "Premises: " + observations + " \n Conclusion: " + shortOfBreath); diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java index 4095736f1..44bf767b1 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java @@ -18,18 +18,15 @@ */ package org.tweetyproject.causal.reasoner; -import org.tweetyproject.arg.dung.semantics.Extension; import org.tweetyproject.causal.semantics.CausalStatement; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.commons.Interpretation; import org.tweetyproject.commons.ModelProvider; import org.tweetyproject.commons.QualitativeReasoner; -import org.tweetyproject.logics.pl.syntax.PlBeliefSet; import org.tweetyproject.logics.pl.syntax.PlFormula; import java.util.Collection; import java.util.HashSet; -import java.util.List; /** * General abstract reasoner for basic causal reasoning with {@link CausalKnowledgeBase} @@ -110,15 +107,57 @@ public Collection getConclusions(CausalKnowledgeBase cbase) { } @Override - public Collection> getModels(CausalKnowledgeBase bbase) { - // TODO implement - return List.of(); + public Collection> getModels(CausalKnowledgeBase cbase) { + return getModels(cbase, new HashSet<>()); } + /** + * Computes the set of all interpretations that can be concluded from the causal knowledge base and observation + * + * @param cbase some causal knowledge base + * @param observation some logical formula over atoms of the causal knowledge base + * @return the set of interpretations that can be concluded from the causal knowledge base + */ + public Collection> getModels(CausalKnowledgeBase cbase, PlFormula observation) { + Collection observations = new HashSet<>(); + observations.add(observation); + return getModels(cbase, observations); + } + + /** + * Computes the set of all interpretations that can be concluded from the causal knowledge base and observations + * + * @param cbase some causal knowledge base + * @param observations some logical formulae over atoms of the causal knowledge base + * @return the set of interpretations that can be concluded from the causal knowledge base + */ + public abstract Collection> getModels(CausalKnowledgeBase cbase, Collection observations); + @Override - public Interpretation getModel(CausalKnowledgeBase bbase) { - // TODO implement - return null; + public Interpretation getModel(CausalKnowledgeBase cbase) { + return getModels(cbase).iterator().next(); + } + + /** + * Computes some interpretation that can be concluded from the causal knowledge base and observations + * + * @param cbase some causal knowledge base + * @param observations some logical formulae over atoms of the causal knowledge base + * @return an interpretation that can be concluded from the causal knowledge base + */ + public Interpretation getModel(CausalKnowledgeBase cbase, Collection observations) { + return getModels(cbase, observations).iterator().next(); + } + + /** + * Computes some interpretation that can be concluded from the causal knowledge base and observations + * + * @param cbase some causal knowledge base + * @param observation some logical formula over atoms of the causal knowledge base + * @return an interpretation that can be concluded from the causal knowledge base + */ + public Interpretation getModel(CausalKnowledgeBase cbase, PlFormula observation) { + return getModels(cbase, observation).iterator().next(); } @Override diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java index c386765f6..eed996c1c 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java @@ -23,8 +23,10 @@ import org.tweetyproject.arg.dung.semantics.Extension; import org.tweetyproject.arg.dung.syntax.Argument; import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.semantics.CausalInterpretation; import org.tweetyproject.causal.syntax.CausalArgument; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.commons.Interpretation; import org.tweetyproject.commons.util.SetTools; import org.tweetyproject.logics.pl.reasoner.AbstractPlReasoner; import org.tweetyproject.logics.pl.reasoner.SimplePlReasoner; @@ -40,12 +42,12 @@ * 'Argumentation-based Causal and Counterfactual Reasoning', * 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), (2022) * - * * @author Lars Bengel */ public class ArgumentationBasedCausalReasoner extends AbstractCausalReasoner { /** Internal reasoner */ protected final AbstractPlReasoner reasoner = new SimplePlReasoner(); + /** Internal reasoner for stable semantics */ protected final AbstractExtensionReasoner stableReasoner = new SimpleStableReasoner(); /** @@ -140,6 +142,8 @@ public boolean query(CausalKnowledgeBase cbase, Collection observatio return true; } + + @Override public Collection getConclusions(CausalKnowledgeBase cbase, Collection observations) { Collection result = new HashSet<>(); @@ -158,4 +162,22 @@ public Collection getConclusions(CausalKnowledgeBase cbase, Collectio } return result; } + + @Override + public Collection> getModels(CausalKnowledgeBase cbase, Collection observations) { + Collection> result = new HashSet<>(); + DungTheory theory = getInducedTheory(cbase, observations); + Collection> extensions = stableReasoner.getModels(theory); + for (Extension extension : extensions) { + CausalInterpretation interpretation = new CausalInterpretation(); + for (Argument argument : extension) { + PlFormula conclusion = ((CausalArgument) argument).getConclusion(); + if (conclusion instanceof Proposition) { + interpretation.add((Proposition) conclusion); + } + } + result.add(interpretation); + } + return result; + } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java index c01772e21..c62f7d6dc 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java @@ -1,4 +1,64 @@ package org.tweetyproject.causal.reasoner; +import org.tweetyproject.arg.dung.reasoner.AbstractExtensionReasoner; +import org.tweetyproject.arg.dung.reasoner.SimpleStableReasoner; +import org.tweetyproject.arg.dung.semantics.Extension; +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.semantics.CounterfactualStatement; +import org.tweetyproject.causal.syntax.CausalArgument; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.causal.syntax.StructuralCausalModel; +import org.tweetyproject.logics.pl.syntax.PlFormula; +import org.tweetyproject.logics.pl.syntax.Proposition; + +import java.util.Collection; +import java.util.Map; + +/** + * + */ public class ArgumentationBasedCounterfactualReasoner { + /** Internal reasoner for stable semantics of the induced AF */ + protected AbstractExtensionReasoner stableReasoner = new SimpleStableReasoner(); + + /** + * + * @param cbase + * @param observations + * @param interventions + * @return + */ + public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations, Map interventions) { + StructuralCausalModel twinModel = cbase.getCausalModel(); + for (Proposition atom : interventions.keySet()) { + twinModel = twinModel.intervene(atom, interventions.get(atom)); + } + return new ArgumentationBasedCausalReasoner().getInducedTheory(new CausalKnowledgeBase(twinModel, cbase.getAssumptions()), observations); + } + + /** + * + * @param cbase + * @param statement + * @return + */ + public boolean query(CausalKnowledgeBase cbase, CounterfactualStatement statement) { + DungTheory theory = getInducedTheory(cbase, statement.getObservations(), statement.getInterventions()); + Collection> extensions = stableReasoner.getModels(theory); + + for (Extension extension : extensions) { + boolean concludesEffect = false; + for (Argument argument : extension) { + if (((CausalArgument) argument).getConclusion().equals(statement.getConclusion())) { + concludesEffect = true; + break; + } + } + if (!concludesEffect) { + return false; + } + } + return true; + } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java new file mode 100644 index 000000000..9eb685b73 --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java @@ -0,0 +1,60 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ +package org.tweetyproject.causal.semantics; + +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.commons.InterpretationSet; +import org.tweetyproject.logics.pl.syntax.PlFormula; +import org.tweetyproject.logics.pl.syntax.Proposition; + +import java.util.Collection; + +/** + * Representation of a propositional interpretation of a causal knowledge base + * + * @author Lars Bengel + * TODO add explicit encoding of rejected atoms + */ +public class CausalInterpretation extends InterpretationSet { + + /** + * Initializes a new empty causal interpretation + */ + public CausalInterpretation() { + super(); + } + + /** + * Initializes a new causal interpretation + * @param formulas the set of proposition that are true in the interpretation + */ + public CausalInterpretation(Collection formulas) { + super(formulas); + } + + @Override + public boolean satisfies(PlFormula formula) throws IllegalArgumentException { + return false; + } + + @Override + public boolean satisfies(CausalKnowledgeBase beliefBase) throws IllegalArgumentException { + return false; + } +} From 6c95b32fe65ecd0c8f3f67ffdbfaefee3f6949d1 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:18:09 +0200 Subject: [PATCH 17/43] simplified causal reasoner implementation --- .../examples/CausalReasoningExampleVirus.java | 2 +- .../reasoner/AbstractCausalReasoner.java | 124 +++++++++++------- .../ArgumentationBasedCausalReasoner.java | 53 +------- 3 files changed, 81 insertions(+), 98 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java index c88f6cd75..1d54e0178 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java @@ -56,7 +56,7 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend System.out.println("Causal Knowledge Base: " + cbase); Collection observations = new HashSet<>(); - //observations.add(fever); + observations.add(fever); // Initialize Causal Reasoner and induce an argumentation framework ArgumentationBasedCausalReasoner reasoner = new ArgumentationBasedCausalReasoner(); diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java index 44bf767b1..f43a51d57 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java @@ -18,12 +18,14 @@ */ package org.tweetyproject.causal.reasoner; +import org.tweetyproject.causal.semantics.CausalInterpretation; import org.tweetyproject.causal.semantics.CausalStatement; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; -import org.tweetyproject.commons.Interpretation; import org.tweetyproject.commons.ModelProvider; import org.tweetyproject.commons.QualitativeReasoner; +import org.tweetyproject.logics.pl.syntax.Negation; import org.tweetyproject.logics.pl.syntax.PlFormula; +import org.tweetyproject.logics.pl.syntax.Proposition; import java.util.Collection; import java.util.HashSet; @@ -33,7 +35,49 @@ * * @author Lars Bengel */ -public abstract class AbstractCausalReasoner implements QualitativeReasoner, ModelProvider> { +public abstract class AbstractCausalReasoner implements QualitativeReasoner, ModelProvider { + /** + * Computes the set of all interpretations that can be concluded from the causal knowledge base and observations + * + * @param cbase some causal knowledge base + * @param observations some logical formulae over atoms of the causal knowledge base + * @return the set of interpretations that can be concluded from the causal knowledge base + */ + public abstract Collection getModels(CausalKnowledgeBase cbase, Collection observations); + + /** + * Computes the set of all literal expressions that can be concluded from the causal knowledge base and observations + * + * @param cbase some causal knowledge base + * @param observations some logical formulae over atoms of the causal knowledge base + * @return the set of expressions that can be concluded from the given knowledge + */ + public Collection getConclusions(CausalKnowledgeBase cbase, Collection observations) { + Collection result = new HashSet<>(); + Collection models = getModels(cbase, observations); + + for (Proposition prop : cbase.getSignature()) { + result.add(prop); + result.add(new Negation(prop)); + } + for (CausalInterpretation model : models) { + for (Proposition prop : cbase.getSignature()) { + if (model.contains(prop)) { + if (!result.contains(prop) && result.contains(new Negation(prop))) { + result.remove(prop); + } + result.remove(new Negation(prop)); + } else { + if (result.contains(prop) && !result.contains(new Negation(prop))) { + result.remove(new Negation(prop)); + } + result.remove(prop); + } + } + } + return result; + } + /** * Determines whether the given effect is entailed by the causal knowledge base together with the observations * @@ -42,7 +86,9 @@ public abstract class AbstractCausalReasoner implements QualitativeReasoner observations, PlFormula effect); + public boolean query(CausalKnowledgeBase cbase, Collection observations, PlFormula effect) { + return getConclusions(cbase, observations).contains(effect); + } /** * Determines whether the given effect is entailed by the causal knowledge base together with the observation @@ -53,9 +99,12 @@ public abstract class AbstractCausalReasoner implements QualitativeReasoner observations = new HashSet<>(); - observations.add(observation); - return query(cbase, observations, effect); + return getConclusions(cbase, observation).contains(effect); + } + + @Override + public Boolean query(CausalKnowledgeBase cbase, PlFormula effect) { + return getConclusions(cbase).contains(effect); } /** @@ -69,19 +118,23 @@ public boolean query(CausalKnowledgeBase cbase, CausalStatement statement) { return query(cbase, statement.getObservations(), statement.getConclusion()); } - @Override - public Boolean query(CausalKnowledgeBase cbase, PlFormula effect) { - return query(cbase, new HashSet<>(), effect); - } - /** - * Computes the set of all literal expressions that can be concluded from the causal knowledge base and observations + * Computes the set of all interpretations that can be concluded from the causal knowledge base and observation * * @param cbase some causal knowledge base - * @param observations some logical formulae over atoms of the causal knowledge base - * @return the set of expressions that can be concluded from the given knowledge + * @param observation some logical formula over atoms of the causal knowledge base + * @return the set of interpretations that can be concluded from the causal knowledge base */ - public abstract Collection getConclusions(CausalKnowledgeBase cbase, Collection observations); + public Collection getModels(CausalKnowledgeBase cbase, PlFormula observation) { + Collection observations = new HashSet<>(); + observations.add(observation); + return getModels(cbase, observations); + } + + @Override + public Collection getModels(CausalKnowledgeBase cbase) { + return getModels(cbase, new HashSet<>()); + } /** * Computes the set of all literal expressions that can be concluded from the causal knowledge base and observation @@ -106,38 +159,6 @@ public Collection getConclusions(CausalKnowledgeBase cbase) { return getConclusions(cbase, new HashSet<>()); } - @Override - public Collection> getModels(CausalKnowledgeBase cbase) { - return getModels(cbase, new HashSet<>()); - } - - /** - * Computes the set of all interpretations that can be concluded from the causal knowledge base and observation - * - * @param cbase some causal knowledge base - * @param observation some logical formula over atoms of the causal knowledge base - * @return the set of interpretations that can be concluded from the causal knowledge base - */ - public Collection> getModels(CausalKnowledgeBase cbase, PlFormula observation) { - Collection observations = new HashSet<>(); - observations.add(observation); - return getModels(cbase, observations); - } - - /** - * Computes the set of all interpretations that can be concluded from the causal knowledge base and observations - * - * @param cbase some causal knowledge base - * @param observations some logical formulae over atoms of the causal knowledge base - * @return the set of interpretations that can be concluded from the causal knowledge base - */ - public abstract Collection> getModels(CausalKnowledgeBase cbase, Collection observations); - - @Override - public Interpretation getModel(CausalKnowledgeBase cbase) { - return getModels(cbase).iterator().next(); - } - /** * Computes some interpretation that can be concluded from the causal knowledge base and observations * @@ -145,7 +166,7 @@ public Interpretation getModel(CausalKnowledgeBa * @param observations some logical formulae over atoms of the causal knowledge base * @return an interpretation that can be concluded from the causal knowledge base */ - public Interpretation getModel(CausalKnowledgeBase cbase, Collection observations) { + public CausalInterpretation getModel(CausalKnowledgeBase cbase, Collection observations) { return getModels(cbase, observations).iterator().next(); } @@ -156,10 +177,15 @@ public Interpretation getModel(CausalKnowledgeBa * @param observation some logical formula over atoms of the causal knowledge base * @return an interpretation that can be concluded from the causal knowledge base */ - public Interpretation getModel(CausalKnowledgeBase cbase, PlFormula observation) { + public CausalInterpretation getModel(CausalKnowledgeBase cbase, PlFormula observation) { return getModels(cbase, observation).iterator().next(); } + @Override + public CausalInterpretation getModel(CausalKnowledgeBase cbase) { + return getModels(cbase).iterator().next(); + } + @Override public boolean isInstalled() { return true; diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java index eed996c1c..d6280e6d3 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java @@ -26,7 +26,6 @@ import org.tweetyproject.causal.semantics.CausalInterpretation; import org.tweetyproject.causal.syntax.CausalArgument; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; -import org.tweetyproject.commons.Interpretation; import org.tweetyproject.commons.util.SetTools; import org.tweetyproject.logics.pl.reasoner.AbstractPlReasoner; import org.tweetyproject.logics.pl.reasoner.SimplePlReasoner; @@ -48,13 +47,13 @@ public class ArgumentationBasedCausalReasoner extends AbstractCausalReasoner { /** Internal reasoner */ protected final AbstractPlReasoner reasoner = new SimplePlReasoner(); /** Internal reasoner for stable semantics */ - protected final AbstractExtensionReasoner stableReasoner = new SimpleStableReasoner(); + protected final AbstractExtensionReasoner extensionReasoner = new SimpleStableReasoner(); /** * Constructs a logical argumentation framework from a given causal knowledge base and some observations * * @param cbase some causal knowledge base - * @param observations some logical formulae representing the observations of causal atoms + * @param observations some logical formulae representing the observations of causal atoms * @return the argumentation framework induced from the causal knowledge base and the observations */ public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations) { @@ -122,52 +121,10 @@ public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations, PlFormula effect) { + public Collection getModels(CausalKnowledgeBase cbase, Collection observations) { + Collection result = new HashSet<>(); DungTheory theory = getInducedTheory(cbase, observations); - Collection> extensions = stableReasoner.getModels(theory); - - //System.out.println(extensions); - for (Extension extension : extensions) { - boolean concludesEffect = false; - for (Argument argument : extension) { - if (((CausalArgument) argument).getConclusion().equals(effect)) { - concludesEffect = true; - break; - } - } - if (!concludesEffect) { - return false; - } - } - return true; - } - - - - @Override - public Collection getConclusions(CausalKnowledgeBase cbase, Collection observations) { - Collection result = new HashSet<>(); - for (Proposition prop : cbase.getSignature()) { - result.add(prop); - result.add(new Negation(prop)); - } - DungTheory theory = getInducedTheory(cbase, observations); - Collection> extensions = stableReasoner.getModels(theory); - for (Extension extension : extensions) { - Collection conclusions = new HashSet<>(); - for (Argument argument : extension) { - conclusions.add(((CausalArgument) argument).getConclusion()); - } - result.retainAll(conclusions); - } - return result; - } - - @Override - public Collection> getModels(CausalKnowledgeBase cbase, Collection observations) { - Collection> result = new HashSet<>(); - DungTheory theory = getInducedTheory(cbase, observations); - Collection> extensions = stableReasoner.getModels(theory); + Collection> extensions = extensionReasoner.getModels(theory); for (Extension extension : extensions) { CausalInterpretation interpretation = new CausalInterpretation(); for (Argument argument : extension) { From 0c349b55fec97b4940ebae16204ad5333341af43 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:04:01 +0200 Subject: [PATCH 18/43] work on counterfactual reasoner --- ...tractArgumentationBasedCausalReasoner.java | 30 +++++++ .../reasoner/AbstractCausalReasoner.java | 11 --- .../ArgumentationBasedCausalReasoner.java | 14 +++- ...umentationBasedCounterfactualReasoner.java | 78 ++++++++++--------- .../semantics/CausalInterpretation.java | 4 +- 5 files changed, 88 insertions(+), 49 deletions(-) create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java new file mode 100644 index 000000000..2a51c217f --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java @@ -0,0 +1,30 @@ +package org.tweetyproject.causal.reasoner; + +import org.tweetyproject.arg.dung.reasoner.AbstractExtensionReasoner; +import org.tweetyproject.arg.dung.reasoner.SimpleStableReasoner; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.logics.pl.reasoner.AbstractPlReasoner; +import org.tweetyproject.logics.pl.reasoner.SimplePlReasoner; +import org.tweetyproject.logics.pl.syntax.PlFormula; + +import java.util.Collection; + +/** + * + */ +public abstract class AbstractArgumentationBasedCausalReasoner extends AbstractCausalReasoner { + /** Internal reasoner */ + protected final AbstractPlReasoner reasoner = new SimplePlReasoner(); + /** Internal reasoner for stable semantics */ + protected final AbstractExtensionReasoner extensionReasoner = new SimpleStableReasoner(); + + /** + * Constructs a logical argumentation framework from a given causal knowledge base and some observations + * + * @param cbase some causal knowledge base + * @param observations some logical formulae representing the observations of causal atoms + * @return the argumentation framework induced from the causal knowledge base and the observations + */ + public abstract DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations); +} diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java index f43a51d57..4bfb54423 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java @@ -107,17 +107,6 @@ public Boolean query(CausalKnowledgeBase cbase, PlFormula effect) { return getConclusions(cbase).contains(effect); } - /** - * Determines whether the given causal statements holds under the causal knowledge base - * - * @param cbase some causal knowledge base - * @param statement some causal statement - * @return TRUE iff the causal statement holds under the causal knowledge base - */ - public boolean query(CausalKnowledgeBase cbase, CausalStatement statement) { - return query(cbase, statement.getObservations(), statement.getConclusion()); - } - /** * Computes the set of all interpretations that can be concluded from the causal knowledge base and observation * diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java index d6280e6d3..e659277bd 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java @@ -24,6 +24,7 @@ import org.tweetyproject.arg.dung.syntax.Argument; import org.tweetyproject.arg.dung.syntax.DungTheory; import org.tweetyproject.causal.semantics.CausalInterpretation; +import org.tweetyproject.causal.semantics.CausalStatement; import org.tweetyproject.causal.syntax.CausalArgument; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.commons.util.SetTools; @@ -43,7 +44,7 @@ * * @author Lars Bengel */ -public class ArgumentationBasedCausalReasoner extends AbstractCausalReasoner { +public class ArgumentationBasedCausalReasoner extends AbstractArgumentationBasedCausalReasoner { /** Internal reasoner */ protected final AbstractPlReasoner reasoner = new SimplePlReasoner(); /** Internal reasoner for stable semantics */ @@ -137,4 +138,15 @@ public Collection getModels(CausalKnowledgeBase cbase, Col } return result; } + + /** + * Determines whether the given causal statements holds under the causal knowledge base + * + * @param cbase some causal knowledge base + * @param statement some causal statement + * @return TRUE iff the causal statement holds under the causal knowledge base + */ + public boolean query(CausalKnowledgeBase cbase, CausalStatement statement) { + return query(cbase, statement.getObservations(), statement.getConclusion()); + } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java index c62f7d6dc..56429f7ea 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java @@ -1,34 +1,41 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ package org.tweetyproject.causal.reasoner; -import org.tweetyproject.arg.dung.reasoner.AbstractExtensionReasoner; -import org.tweetyproject.arg.dung.reasoner.SimpleStableReasoner; -import org.tweetyproject.arg.dung.semantics.Extension; -import org.tweetyproject.arg.dung.syntax.Argument; import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.semantics.CausalInterpretation; +import org.tweetyproject.causal.semantics.CausalStatement; import org.tweetyproject.causal.semantics.CounterfactualStatement; -import org.tweetyproject.causal.syntax.CausalArgument; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.causal.syntax.StructuralCausalModel; import org.tweetyproject.logics.pl.syntax.PlFormula; import org.tweetyproject.logics.pl.syntax.Proposition; import java.util.Collection; +import java.util.HashMap; import java.util.Map; /** * */ -public class ArgumentationBasedCounterfactualReasoner { - /** Internal reasoner for stable semantics of the induced AF */ - protected AbstractExtensionReasoner stableReasoner = new SimpleStableReasoner(); +public class ArgumentationBasedCounterfactualReasoner extends AbstractArgumentationBasedCausalReasoner { - /** - * - * @param cbase - * @param observations - * @param interventions - * @return - */ public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations, Map interventions) { StructuralCausalModel twinModel = cbase.getCausalModel(); for (Proposition atom : interventions.keySet()) { @@ -37,28 +44,29 @@ public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations) { + return getInducedTheory(cbase, observations, new HashMap<>()); + } + + + @Override + public Collection getModels(CausalKnowledgeBase cbase, Collection observations) { + return null; + } + + public boolean query(CausalKnowledgeBase cbase, CounterfactualStatement statement) { + return query(cbase, statement.getObservations(), statement.getInterventions(), statement.getConclusion()); + } + /** + * Determines whether the given causal statements holds under the causal knowledge base * - * @param cbase - * @param statement - * @return + * @param cbase some causal knowledge base + * @param statement some causal statement + * @return TRUE iff the causal statement holds under the causal knowledge base */ - public boolean query(CausalKnowledgeBase cbase, CounterfactualStatement statement) { - DungTheory theory = getInducedTheory(cbase, statement.getObservations(), statement.getInterventions()); - Collection> extensions = stableReasoner.getModels(theory); - - for (Extension extension : extensions) { - boolean concludesEffect = false; - for (Argument argument : extension) { - if (((CausalArgument) argument).getConclusion().equals(statement.getConclusion())) { - concludesEffect = true; - break; - } - } - if (!concludesEffect) { - return false; - } - } - return true; + public boolean query(CausalKnowledgeBase cbase, CausalStatement statement) { + return query(cbase, statement.getObservations(), statement.getConclusion()); } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java index 9eb685b73..42dbb49de 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java @@ -50,11 +50,11 @@ public CausalInterpretation(Collection formulas) { @Override public boolean satisfies(PlFormula formula) throws IllegalArgumentException { - return false; + return this.contains(formula); } @Override public boolean satisfies(CausalKnowledgeBase beliefBase) throws IllegalArgumentException { - return false; + throw new IllegalArgumentException("Satisfaction of belief bases by causal interpretations is undefined."); } } From 820bd77334045ff769470a24ce2aa18914f7a8fd Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:22:03 +0100 Subject: [PATCH 19/43] incorporated interventions into causal reasoning --- ...tractArgumentationBasedCausalReasoner.java | 50 +++++++++- .../reasoner/AbstractCausalReasoner.java | 92 ++++++++++++++----- .../ArgumentationBasedCausalReasoner.java | 59 +++++------- .../semantics/CounterfactualStatement.java | 11 --- .../causal/syntax/StructuralCausalModel.java | 2 +- 5 files changed, 140 insertions(+), 74 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java index 2a51c217f..2e148fb8a 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java @@ -2,16 +2,30 @@ import org.tweetyproject.arg.dung.reasoner.AbstractExtensionReasoner; import org.tweetyproject.arg.dung.reasoner.SimpleStableReasoner; +import org.tweetyproject.arg.dung.semantics.Extension; +import org.tweetyproject.arg.dung.syntax.Argument; import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.semantics.CausalInterpretation; +import org.tweetyproject.causal.syntax.CausalArgument; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.logics.pl.reasoner.AbstractPlReasoner; import org.tweetyproject.logics.pl.reasoner.SimplePlReasoner; import org.tweetyproject.logics.pl.syntax.PlFormula; +import org.tweetyproject.logics.pl.syntax.Proposition; import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; /** + * Base class for argumentation-based causal reasoners as described in
+ *
+ * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, + * 'Argumentation-based Causal and Counterfactual Reasoning', + * 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), (2022) * + * @author Lars Bengel */ public abstract class AbstractArgumentationBasedCausalReasoner extends AbstractCausalReasoner { /** Internal reasoner */ @@ -22,9 +36,39 @@ public abstract class AbstractArgumentationBasedCausalReasoner extends AbstractC /** * Constructs a logical argumentation framework from a given causal knowledge base and some observations * - * @param cbase some causal knowledge base - * @param observations some logical formulae representing the observations of causal atoms + * @param cbase some causal knowledge base + * @param observations some logical formulae representing the observations of causal atoms + * @param interventions a set of interventions on causal atoms * @return the argumentation framework induced from the causal knowledge base and the observations */ - public abstract DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations); + public abstract DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations, Map interventions); + + /** + * Constructs a logical argumentation framework from a given causal knowledge base and some observations + * + * @param cbase some causal knowledge base + * @param observations some logical formulae representing the observations of causal atoms + * @return the argumentation framework induced from the causal knowledge base and the observations + */ + public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations) { + return getInducedTheory(cbase, observations, new HashMap<>()); + } + + @Override + public Collection getModels(CausalKnowledgeBase cbase, Collection observations, Map interventions) { + Collection result = new HashSet<>(); + DungTheory theory = this.getInducedTheory(cbase, observations, interventions); + Collection> extensions = extensionReasoner.getModels(theory); + for (Extension extension : extensions) { + CausalInterpretation interpretation = new CausalInterpretation(); + for (Argument argument : extension) { + PlFormula conclusion = ((CausalArgument) argument).getConclusion(); + if (conclusion instanceof Proposition) { + interpretation.add((Proposition) conclusion); + } + } + result.add(interpretation); + } + return result; + } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java index 4bfb54423..556472bce 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractCausalReasoner.java @@ -19,7 +19,6 @@ package org.tweetyproject.causal.reasoner; import org.tweetyproject.causal.semantics.CausalInterpretation; -import org.tweetyproject.causal.semantics.CausalStatement; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.commons.ModelProvider; import org.tweetyproject.commons.QualitativeReasoner; @@ -28,7 +27,9 @@ import org.tweetyproject.logics.pl.syntax.Proposition; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; /** * General abstract reasoner for basic causal reasoning with {@link CausalKnowledgeBase} @@ -41,20 +42,22 @@ public abstract class AbstractCausalReasoner implements QualitativeReasoner getModels(CausalKnowledgeBase cbase, Collection observations); + public abstract Collection getModels(CausalKnowledgeBase cbase, Collection observations, Map interventions); /** * Computes the set of all literal expressions that can be concluded from the causal knowledge base and observations * * @param cbase some causal knowledge base * @param observations some logical formulae over atoms of the causal knowledge base + * @param interventions a set of interventions on causal atoms * @return the set of expressions that can be concluded from the given knowledge */ - public Collection getConclusions(CausalKnowledgeBase cbase, Collection observations) { + public Collection getConclusions(CausalKnowledgeBase cbase, Collection observations, Map interventions) { Collection result = new HashSet<>(); - Collection models = getModels(cbase, observations); + Collection models = getModels(cbase, observations, interventions); for (Proposition prop : cbase.getSignature()) { result.add(prop); @@ -78,6 +81,53 @@ public Collection getConclusions(CausalKnowledgeBase cbase, Collectio return result; } + /** + * Computes the set of all literal expressions that can be concluded from the causal knowledge base and observation + * + * @param cbase some causal knowledge base + * @param observations some logical formula over atoms of the causal knowledge base + * @return the set of expressions that can be concluded from the given knowledge + */ + public Collection getConclusions(CausalKnowledgeBase cbase, Collection observations) { + return getConclusions(cbase, observations, new HashMap<>()); + } + + /** + * Computes the set of all literal expressions that can be concluded from the causal knowledge base and observation + * + * @param cbase some causal knowledge base + * @param observation some logical formula over atoms of the causal knowledge base + * @return the set of expressions that can be concluded from the given knowledge + */ + public Collection getConclusions(CausalKnowledgeBase cbase, PlFormula observation) { + Collection observations = new HashSet<>(); + observations.add(observation); + return getConclusions(cbase, observations); + } + + /** + * Computes the set of all literal expressions that can be concluded from the causal knowledge base + * + * @param cbase some causal knowledge base + * @return the set of expressions that can be concluded from the given knowledge + */ + public Collection getConclusions(CausalKnowledgeBase cbase) { + return getConclusions(cbase, new HashSet<>()); + } + + /** + * Determines whether the given effect is entailed by the causal knowledge base together with the observations + * + * @param cbase some causal knowledge base + * @param observations some logical formulae over atoms of the causal knowledge base + * @param interventions a set of interventions on causal atoms + * @param effect some logical formula over atoms of the causal knowledge base + * @return TRUE iff the causal knowledge base together with the observations logically entails the effect + */ + public boolean query(CausalKnowledgeBase cbase, Collection observations, Map interventions, PlFormula effect) { + return getConclusions(cbase, observations, interventions).contains(effect); + } + /** * Determines whether the given effect is entailed by the causal knowledge base together with the observations * @@ -107,6 +157,17 @@ public Boolean query(CausalKnowledgeBase cbase, PlFormula effect) { return getConclusions(cbase).contains(effect); } + /** + * Computes the set of all interpretations that can be concluded from the causal knowledge base and observation + * + * @param cbase some causal knowledge base + * @param observations some logical formula over atoms of the causal knowledge base + * @return the set of interpretations that can be concluded from the causal knowledge base + */ + public Collection getModels(CausalKnowledgeBase cbase, Collection observations) { + return getModels(cbase, observations, new HashMap<>()); + } + /** * Computes the set of all interpretations that can be concluded from the causal knowledge base and observation * @@ -126,26 +187,15 @@ public Collection getModels(CausalKnowledgeBase cbase) { } /** - * Computes the set of all literal expressions that can be concluded from the causal knowledge base and observation + * Computes some interpretation that can be concluded from the causal knowledge base and observations * * @param cbase some causal knowledge base - * @param observation some logical formula over atoms of the causal knowledge base - * @return the set of expressions that can be concluded from the given knowledge - */ - public Collection getConclusions(CausalKnowledgeBase cbase, PlFormula observation) { - Collection observations = new HashSet<>(); - observations.add(observation); - return getConclusions(cbase, observations); - } - - /** - * Computes the set of all literal expressions that can be concluded from the causal knowledge base - * - * @param cbase some causal knowledge base - * @return the set of expressions that can be concluded from the given knowledge + * @param observations some logical formulae over atoms of the causal knowledge base + * @param interventions a set of interventions on causal atoms + * @return an interpretation that can be concluded from the causal knowledge base */ - public Collection getConclusions(CausalKnowledgeBase cbase) { - return getConclusions(cbase, new HashSet<>()); + public CausalInterpretation getModel(CausalKnowledgeBase cbase, Collection observations, Map interventions) { + return getModels(cbase, observations, interventions).iterator().next(); } /** diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java index e659277bd..126c708a8 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasoner.java @@ -18,22 +18,19 @@ */ package org.tweetyproject.causal.reasoner; -import org.tweetyproject.arg.dung.reasoner.AbstractExtensionReasoner; -import org.tweetyproject.arg.dung.reasoner.SimpleStableReasoner; -import org.tweetyproject.arg.dung.semantics.Extension; import org.tweetyproject.arg.dung.syntax.Argument; import org.tweetyproject.arg.dung.syntax.DungTheory; -import org.tweetyproject.causal.semantics.CausalInterpretation; import org.tweetyproject.causal.semantics.CausalStatement; +import org.tweetyproject.causal.semantics.InterventionalStatement; import org.tweetyproject.causal.syntax.CausalArgument; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.causal.syntax.StructuralCausalModel; import org.tweetyproject.commons.util.SetTools; -import org.tweetyproject.logics.pl.reasoner.AbstractPlReasoner; -import org.tweetyproject.logics.pl.reasoner.SimplePlReasoner; import org.tweetyproject.logics.pl.syntax.*; import java.util.Collection; import java.util.HashSet; +import java.util.Map; /** * Implements the approach to argumentation-based causal reasoning as described in
@@ -45,20 +42,13 @@ * @author Lars Bengel */ public class ArgumentationBasedCausalReasoner extends AbstractArgumentationBasedCausalReasoner { - /** Internal reasoner */ - protected final AbstractPlReasoner reasoner = new SimplePlReasoner(); - /** Internal reasoner for stable semantics */ - protected final AbstractExtensionReasoner extensionReasoner = new SimpleStableReasoner(); - - /** - * Constructs a logical argumentation framework from a given causal knowledge base and some observations - * - * @param cbase some causal knowledge base - * @param observations some logical formulae representing the observations of causal atoms - * @return the argumentation framework induced from the causal knowledge base and the observations - */ - public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations) { - PlBeliefSet base = new PlBeliefSet(cbase.getBeliefs()); + @Override + public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations, Map interventions) { + StructuralCausalModel model = cbase.getCausalModel(); + for (Proposition atom : interventions.keySet()) { + model = model.intervene(atom, interventions.get(atom)); + } + PlBeliefSet base = new PlBeliefSet(model.getStructuralEquations()); base.addAll(observations); Collection literals = new HashSet<>(); @@ -121,24 +111,6 @@ public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection getModels(CausalKnowledgeBase cbase, Collection observations) { - Collection result = new HashSet<>(); - DungTheory theory = getInducedTheory(cbase, observations); - Collection> extensions = extensionReasoner.getModels(theory); - for (Extension extension : extensions) { - CausalInterpretation interpretation = new CausalInterpretation(); - for (Argument argument : extension) { - PlFormula conclusion = ((CausalArgument) argument).getConclusion(); - if (conclusion instanceof Proposition) { - interpretation.add((Proposition) conclusion); - } - } - result.add(interpretation); - } - return result; - } - /** * Determines whether the given causal statements holds under the causal knowledge base * @@ -149,4 +121,15 @@ public Collection getModels(CausalKnowledgeBase cbase, Col public boolean query(CausalKnowledgeBase cbase, CausalStatement statement) { return query(cbase, statement.getObservations(), statement.getConclusion()); } + + /** + * Determines whether the given causal statements holds under the causal knowledge base + * + * @param cbase some causal knowledge base + * @param statement some causal statement with interventions + * @return TRUE iff the causal statement holds under the causal knowledge base + */ + public boolean query(CausalKnowledgeBase cbase, InterventionalStatement statement) { + return query(cbase, statement.getObservations(), statement.getInterventions(), statement.getConclusion()); + } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java index 1e4495edf..fe9284871 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CounterfactualStatement.java @@ -74,15 +74,4 @@ public boolean addCounterfactualIntervention(Proposition atom, boolean value) { Proposition cAtom = new Proposition(atom.getName()+"*"); return Boolean.TRUE.equals(this.interventions.put(cAtom, value)); } - - /* - private boolean checkCounterFactualStatement(CausalKnowledgeBase ckbase, PlFormula conclusion) { - StructuralCausalModel twinModel = ckbase.getCausalModel().getTwinModel(); - for (Proposition atom : this.interventions.keySet()) { - twinModel = twinModel.intervene(new Proposition(atom.getName()+"*"), interventions.get(atom)); - } - return new CausalKnowledgeBase(twinModel, ckbase.getAssumptions()).entails(this.getObservations(), conclusion); - } - - */ } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java index 2308bb0b2..44281b30d 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java @@ -67,7 +67,7 @@ public StructuralCausalModel(Collection equations) throws CyclicDepen if (formula instanceof Equivalence) { Equivalence equation = (Equivalence) formula; PlFormula left = equation.getFormulas().getFirst(); - if (!left.isLiteral()) { + if (!(left instanceof Proposition)) { // TODO allow literal here throw new IllegalArgumentException("Left side of Structural Equation must be literal!"); } explainable.addAll(left.getAtoms()); From d0cec602fb3f91068721bab2e61912d350831f05 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:22:15 +0100 Subject: [PATCH 20/43] implemented counterfactual reasoner --- ...umentationBasedCounterfactualReasoner.java | 39 +++++-------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java index 56429f7ea..634ef5435 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java @@ -19,8 +19,6 @@ package org.tweetyproject.causal.reasoner; import org.tweetyproject.arg.dung.syntax.DungTheory; -import org.tweetyproject.causal.semantics.CausalInterpretation; -import org.tweetyproject.causal.semantics.CausalStatement; import org.tweetyproject.causal.semantics.CounterfactualStatement; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.causal.syntax.StructuralCausalModel; @@ -28,45 +26,26 @@ import org.tweetyproject.logics.pl.syntax.Proposition; import java.util.Collection; -import java.util.HashMap; import java.util.Map; /** + * Implements the approach to argumentation-based counterfactual reasoning as described in
+ *
+ * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, + * 'Argumentation-based Causal and Counterfactual Reasoning', + * 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), (2022) * + * @author Lars Bengel */ public class ArgumentationBasedCounterfactualReasoner extends AbstractArgumentationBasedCausalReasoner { - public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations, Map interventions) { - StructuralCausalModel twinModel = cbase.getCausalModel(); - for (Proposition atom : interventions.keySet()) { - twinModel = twinModel.intervene(atom, interventions.get(atom)); - } - return new ArgumentationBasedCausalReasoner().getInducedTheory(new CausalKnowledgeBase(twinModel, cbase.getAssumptions()), observations); - } - - @Override - public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations) { - return getInducedTheory(cbase, observations, new HashMap<>()); - } - - @Override - public Collection getModels(CausalKnowledgeBase cbase, Collection observations) { - return null; + public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations, Map interventions) { + StructuralCausalModel twinModel = cbase.getCausalModel().getTwinModel(); + return new ArgumentationBasedCausalReasoner().getInducedTheory(new CausalKnowledgeBase(twinModel, cbase.getAssumptions()), observations, interventions); } public boolean query(CausalKnowledgeBase cbase, CounterfactualStatement statement) { return query(cbase, statement.getObservations(), statement.getInterventions(), statement.getConclusion()); } - - /** - * Determines whether the given causal statements holds under the causal knowledge base - * - * @param cbase some causal knowledge base - * @param statement some causal statement - * @return TRUE iff the causal statement holds under the causal knowledge base - */ - public boolean query(CausalKnowledgeBase cbase, CausalStatement statement) { - return query(cbase, statement.getObservations(), statement.getConclusion()); - } } From cd831db45fb15efab009a8e7b7bb67d332cb2584 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:32:53 +0100 Subject: [PATCH 21/43] adjusted examples --- .../CausalReasoningExampleSurfer.java | 18 ++++++++++++++---- .../examples/CausalReasoningExampleVirus.java | 19 ++++++++++++++----- .../causal/syntax/StructuralCausalModel.java | 1 - 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java index 7c9f7f33f..f41e5355a 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java @@ -10,6 +10,12 @@ import java.util.HashSet; /** + * Example usage of the {@link ArgumentationBasedCausalReasoner} based on the example from
+ *
+ * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, + * 'Argumentation-Based Probabilistic Causal Reasoning', + * Conference on Advances in Robust Argumentation Machines, (2024) + * * @author Lars Bengel */ public class CausalReasoningExampleSurfer { @@ -51,9 +57,13 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend System.out.println("Causal Knowledge Base: " + cbase); + // Define variables for the example Collection observations = new HashSet<>(); observations.add(drowning); + PlFormula conclusion1 = submersion; + PlFormula conclusion2 = new Negation(submersion); + // Initialize Causal Reasoner and induce an argumentation framework ArgumentationBasedCausalReasoner reasoner = new ArgumentationBasedCausalReasoner(); DungTheory theory = reasoner.getInducedTheory(cbase, observations); @@ -62,9 +72,9 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend System.out.println(theory.prettyPrint()); // Do some causal reasoning - System.out.println("Observing 'drowning' implies 'submersion': " + reasoner.query(cbase, observations, cramp)); - System.out.println("Observing 'drowning' implies 'not submersion': " + reasoner.query(cbase, observations, new Negation(submersion))); - - System.out.printf("Possible Conclusions of observing '%2$s': %1$s", reasoner.getConclusions(cbase, observations), observations); + System.out.printf("Observing '%s' implies '%s': %s%n", observations, conclusion1, reasoner.query(cbase, observations, conclusion1)); + System.out.printf("Observing '%s' implies '%s': %s%n", observations, conclusion2, reasoner.query(cbase, observations, conclusion2)); + System.out.printf("Possible Conclusions of observing '%1$s': %2$s", observations, reasoner.getConclusions(cbase, observations)); + System.out.printf("Models: %s%n", reasoner.getModels(cbase, observations)); } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java index 1d54e0178..933b337b1 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java @@ -11,7 +11,13 @@ import java.util.HashSet; /** + * Example usage of the {@link ArgumentationBasedCausalReasoner} based on the example from
+ *
+ * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, + * 'Argumentation-based Causal and Counterfactual Reasoning', + * 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), (2022) * + * @author Lars Bengel */ public class CausalReasoningExampleVirus { /** @@ -55,9 +61,14 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend System.out.println("Causal Knowledge Base: " + cbase); + + // Define variables for the example Collection observations = new HashSet<>(); observations.add(fever); + PlFormula conclusion1 = shortOfBreath; + PlFormula conclusion2 = new Negation(shortOfBreath); + // Initialize Causal Reasoner and induce an argumentation framework ArgumentationBasedCausalReasoner reasoner = new ArgumentationBasedCausalReasoner(); DungTheory theory = reasoner.getInducedTheory(cbase,observations); @@ -66,11 +77,9 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend System.out.println(theory.prettyPrint()); // Do some causal reasoning - System.out.printf("Observing '%1$s' implies 'shortOfBreath': %2$s%n", observations, reasoner.query(cbase, observations, shortOfBreath)); - System.out.printf("Observing '%1$s' implies 'not shortOfBreath': %2$s%n", observations, reasoner.query(cbase, observations, new Negation(shortOfBreath))); - - System.out.printf("Possible Conclusions of observing '%2$s': %1$s%n", reasoner.getConclusions(cbase, observations), observations); - + System.out.printf("Observing '%1$s' implies '%2$s': %3$s%n", observations, conclusion1, reasoner.query(cbase, observations, conclusion1)); + System.out.printf("Observing '%1$s' implies '%2$s': %3$s%n", observations, conclusion2, reasoner.query(cbase, observations, conclusion2)); + System.out.printf("Possible Conclusions of observing '%1$s': %2$s%n", observations, reasoner.getConclusions(cbase, observations)); System.out.printf("Models: %s%n", reasoner.getModels(cbase, observations)); // Visualisation of the induced argumentation framework diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java index 44281b30d..279c464a7 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java @@ -31,7 +31,6 @@ * * @author Julian Sander * @author Lars Bengel - * * TODO explainableAtoms needs to store somehow whether the atom is to be negated in the equation * TODO or the equation must be transformed before storing */ From bcd8b3217affdb261132823d9f207c937c96a5ec Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:35:01 +0100 Subject: [PATCH 22/43] bugfix --- .../causal/examples/CausalReasoningExampleSurfer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java index f41e5355a..1f0cb5dcc 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java @@ -74,7 +74,7 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend // Do some causal reasoning System.out.printf("Observing '%s' implies '%s': %s%n", observations, conclusion1, reasoner.query(cbase, observations, conclusion1)); System.out.printf("Observing '%s' implies '%s': %s%n", observations, conclusion2, reasoner.query(cbase, observations, conclusion2)); - System.out.printf("Possible Conclusions of observing '%1$s': %2$s", observations, reasoner.getConclusions(cbase, observations)); + System.out.printf("Possible Conclusions of observing '%1$s': %2$s%n", observations, reasoner.getConclusions(cbase, observations)); System.out.printf("Models: %s%n", reasoner.getModels(cbase, observations)); } } From 42bc0b84b966937dbaeef493792db46ec565cd83 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:05:10 +0100 Subject: [PATCH 23/43] added function to create twin atoms consistently --- .../causal/syntax/StructuralCausalModel.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java index 279c464a7..d5d37da92 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java @@ -177,6 +177,21 @@ public boolean addExplainableAtom(PlFormula atom, PlFormula cause) throws Cyclic return !cause.equals(ret); } + /** + * Converts the given causal atom to a counterfactual copy + * @param proposition some causal atom + * @return the counterfactual copy of the given causal atom + */ + public Proposition getCounterfactualCopy(Proposition proposition) { + if (this.getBackgroundAtoms().contains(proposition)) { + return proposition; + } else if (this.getExplainableAtoms().contains(proposition)) { + return new Proposition(proposition.getName() + "*"); + } else { + throw new IllegalArgumentException("Proposition is not contained in causal model"); + } + } + /** * Constructs the twin model for this causal model, i.e., it creates for each structural equation a copy where all * explainable atoms X are replaced by a twin version X* @@ -200,11 +215,11 @@ public StructuralCausalModel getTwinModel() { PlFormula atom = ((Equivalence) equation).getFormulas().getFirst(); PlFormula cause = ((Equivalence) equation).getFormulas().getSecond(); if (atom instanceof Proposition) { - Proposition twinAtom = new Proposition(((Proposition) atom).getName() + "*"); + Proposition twinAtom = getCounterfactualCopy((Proposition) atom); PlFormula twinCause = cause.clone(); for (Proposition prop : cause.getAtoms()) { if (model.getBackgroundAtoms().contains(prop)) continue; - twinCause = twinCause.replace(prop, new Proposition(((Proposition) atom).getName() + "*"), cause.numberOfOccurrences(prop)); + twinCause = twinCause.replace(prop, getCounterfactualCopy((Proposition) atom), cause.numberOfOccurrences(prop)); } twinEquations.put(twinAtom, twinCause); } else { From 64eabbec7e12e46bd8937bb4bc1c3700b3635608 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:05:32 +0100 Subject: [PATCH 24/43] updated examples for all reasoning types --- .../CausalReasoningExampleSurfer.java | 18 +++ .../examples/CausalReasoningExampleVirus.java | 18 +++ .../CounterfactualReasoningExample.java | 111 ++++++++++++++++++ .../InterventionalCausalReasoningExample.java | 107 +++++++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CounterfactualReasoningExample.java create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/InterventionalCausalReasoningExample.java diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java index 1f0cb5dcc..39f485075 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java @@ -1,3 +1,21 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ package org.tweetyproject.causal.examples; import org.tweetyproject.arg.dung.syntax.DungTheory; diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java index 933b337b1..b0e06af15 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java @@ -1,3 +1,21 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ package org.tweetyproject.causal.examples; import org.tweetyproject.arg.dung.syntax.DungTheory; diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CounterfactualReasoningExample.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CounterfactualReasoningExample.java new file mode 100644 index 000000000..08e48a8f6 --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CounterfactualReasoningExample.java @@ -0,0 +1,111 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ +package org.tweetyproject.causal.examples; + +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.reasoner.AbstractArgumentationBasedCausalReasoner; +import org.tweetyproject.causal.reasoner.ArgumentationBasedCausalReasoner; +import org.tweetyproject.causal.reasoner.ArgumentationBasedCounterfactualReasoner; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.causal.syntax.StructuralCausalModel; +import org.tweetyproject.logics.pl.syntax.*; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +/** + * Example usage of the {@link ArgumentationBasedCounterfactualReasoner} based on the example from
+ *
+ * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, + * 'Argumentation-based Causal and Counterfactual Reasoning', + * 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), (2022) + * + * @author Lars Bengel + */ +public class CounterfactualReasoningExample { + /** + * + * @param args cmdline arguments (unused) + */ + public static void main(String[] args) throws StructuralCausalModel.CyclicDependencyException { + // Background atoms + Proposition corona = new Proposition("corona"); + Proposition influenza = new Proposition("influenza"); + Proposition atRisk = new Proposition("at-risk"); + + // Explainable atoms + Proposition covid = new Proposition("covid"); + Proposition flu = new Proposition("flu"); + Proposition shortOfBreath = new Proposition("short-of-breath"); + Proposition fever = new Proposition("fever"); + Proposition chills = new Proposition("chills"); + + // Construct Causal Model + StructuralCausalModel model = new StructuralCausalModel(); + // Add background atoms + model.addBackgroundAtom(corona); + model.addBackgroundAtom(influenza); + model.addBackgroundAtom(atRisk); + // Add structural equations + model.addExplainableAtom(covid, corona); + model.addExplainableAtom(flu, influenza); + model.addExplainableAtom(fever, new Disjunction(covid, flu)); + model.addExplainableAtom(chills, fever); + model.addExplainableAtom(shortOfBreath, new Conjunction(covid, atRisk)); + + // Initialize causal knowledge base + CausalKnowledgeBase cbase = new CausalKnowledgeBase(model); + // Add assumptions + cbase.addAssumption(new Negation(corona)); + cbase.addAssumption(new Negation(influenza)); + cbase.addAssumption(new Negation(atRisk)); + cbase.addAssumption(atRisk); + + System.out.println("Causal Knowledge Base: " + cbase); + + // Initialize Causal Reasoner and induce an argumentation framework + AbstractArgumentationBasedCausalReasoner reasoner = new ArgumentationBasedCounterfactualReasoner(); + + // Define variables for the example + Collection observations = new HashSet<>(); + observations.add(shortOfBreath); + + PlFormula conclusion1 = model.getCounterfactualCopy(fever); + PlFormula conclusion2 = new Negation(model.getCounterfactualCopy(fever)); + + Map interventions = new HashMap<>(); + interventions.put(model.getCounterfactualCopy(covid), false); + + + // Example usage + DungTheory theory = reasoner.getInducedTheory(cbase,observations, interventions); + System.out.println("Induced Argumentation Framework:\n" + theory.prettyPrint()); + + System.out.printf("Observing '%1$s' and intervening '%2$s' implies '%3$s': %4$s%n", observations, interventions, conclusion1, reasoner.query(cbase, observations, interventions, conclusion1)); + System.out.printf("Observing '%1$s' and intervening '%2$s' implies '%3$s': %4$s%n", observations, interventions, conclusion2, reasoner.query(cbase, observations, interventions, conclusion2)); + + // modify observations + observations.add(shortOfBreath); + + System.out.printf("Observing '%1$s' and intervening '%2$s' implies '%3$s': %4$s%n", observations, interventions, conclusion1, reasoner.query(cbase, observations, interventions, conclusion1)); + + } +} diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/InterventionalCausalReasoningExample.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/InterventionalCausalReasoningExample.java new file mode 100644 index 000000000..3dbd61782 --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/InterventionalCausalReasoningExample.java @@ -0,0 +1,107 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ +package org.tweetyproject.causal.examples; + +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.reasoner.AbstractArgumentationBasedCausalReasoner; +import org.tweetyproject.causal.reasoner.ArgumentationBasedCausalReasoner; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.causal.syntax.StructuralCausalModel; +import org.tweetyproject.logics.pl.syntax.*; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +/** + * Example usage of the {@link ArgumentationBasedCausalReasoner} based on the example from
+ *
+ * Lars Bengel, Lydia Blümel, Tjitze Rienstra and Matthias Thimm, + * 'Argumentation-based Causal and Counterfactual Reasoning', + * 1st International Workshop on Argumentation for eXplainable AI (ArgXAI), (2022) + * + * @author Lars Bengel + */ +public class InterventionalCausalReasoningExample { + /** + * + * @param args cmdline arguments (unused) + */ + public static void main(String[] args) throws StructuralCausalModel.CyclicDependencyException { + // Background atoms + Proposition corona = new Proposition("corona"); + Proposition influenza = new Proposition("influenza"); + Proposition atRisk = new Proposition("at-risk"); + + // Explainable atoms + Proposition covid = new Proposition("covid"); + Proposition flu = new Proposition("flu"); + Proposition shortOfBreath = new Proposition("short-of-breath"); + Proposition fever = new Proposition("fever"); + Proposition chills = new Proposition("chills"); + + // Construct Causal Model + StructuralCausalModel model = new StructuralCausalModel(); + // Add background atoms + model.addBackgroundAtom(corona); + model.addBackgroundAtom(influenza); + model.addBackgroundAtom(atRisk); + // Add structural equations + model.addExplainableAtom(covid, corona); + model.addExplainableAtom(flu, influenza); + model.addExplainableAtom(fever, new Disjunction(covid, flu)); + model.addExplainableAtom(chills, fever); + model.addExplainableAtom(shortOfBreath, new Conjunction(covid, atRisk)); + + // Initialize causal knowledge base + CausalKnowledgeBase cbase = new CausalKnowledgeBase(model); + // Add assumptions + cbase.addAssumption(new Negation(corona)); + cbase.addAssumption(new Negation(influenza)); + cbase.addAssumption(new Negation(atRisk)); + cbase.addAssumption(atRisk); + + System.out.println("Causal Knowledge Base: " + cbase); + + // Initialize Causal Reasoner and induce an argumentation framework + AbstractArgumentationBasedCausalReasoner reasoner = new ArgumentationBasedCausalReasoner(); + + // Define variables for the example + Collection observations = new HashSet<>(); + observations.add(shortOfBreath); + + PlFormula conclusion1 = chills; + PlFormula conclusion2 = new Negation(chills); + + Map interventions = new HashMap<>(); + interventions.put(fever, false); + + // Example usage + DungTheory theory = reasoner.getInducedTheory(cbase,observations,interventions); + System.out.println("Induced Argumentation Framework:\n" + theory.prettyPrint()); + + System.out.printf("Observing '%1$s' implies '%2$s': %3$s%n", observations, conclusion1, reasoner.query(cbase, observations, conclusion1)); + System.out.printf("Observing '%1$s' and intervening '%2$s' implies '%3$s': %4$s%n", observations, interventions, conclusion2, reasoner.query(cbase, observations, interventions, conclusion2)); + + System.out.printf("Possible Conclusions of observing '%1$s': %2$s%n", observations, reasoner.getConclusions(cbase, observations)); + System.out.printf("Models: %s%n", reasoner.getModels(cbase, observations)); + + } +} From 63f761ece3905585df6cbfe056c8ca587244ef14 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:39:09 +0100 Subject: [PATCH 25/43] bugfix counterfactual reasoning --- ...umentationBasedCounterfactualReasoner.java | 40 ++++++++++++++++++- .../causal/syntax/CausalKnowledgeBase.java | 4 ++ .../causal/syntax/StructuralCausalModel.java | 23 ++++++----- 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java index 634ef5435..478d4068b 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java @@ -19,13 +19,16 @@ package org.tweetyproject.causal.reasoner; import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.semantics.CausalInterpretation; import org.tweetyproject.causal.semantics.CounterfactualStatement; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.causal.syntax.StructuralCausalModel; +import org.tweetyproject.logics.pl.syntax.Negation; import org.tweetyproject.logics.pl.syntax.PlFormula; import org.tweetyproject.logics.pl.syntax.Proposition; import java.util.Collection; +import java.util.HashSet; import java.util.Map; /** @@ -41,11 +44,44 @@ public class ArgumentationBasedCounterfactualReasoner extends AbstractArgumentat @Override public DungTheory getInducedTheory(CausalKnowledgeBase cbase, Collection observations, Map interventions) { - StructuralCausalModel twinModel = cbase.getCausalModel().getTwinModel(); - return new ArgumentationBasedCausalReasoner().getInducedTheory(new CausalKnowledgeBase(twinModel, cbase.getAssumptions()), observations, interventions); + return new ArgumentationBasedCausalReasoner().getInducedTheory(cbase, observations, interventions); } public boolean query(CausalKnowledgeBase cbase, CounterfactualStatement statement) { return query(cbase, statement.getObservations(), statement.getInterventions(), statement.getConclusion()); } + + @Override + public Collection getModels(CausalKnowledgeBase cbase, Collection observations, Map interventions) { + return super.getModels(cbase.getTwinVersion(), observations, interventions); + } + + @Override + public Collection getConclusions(CausalKnowledgeBase cbase, Collection observations, Map interventions) { + Collection result = new HashSet<>(); + Collection models = getModels(cbase, observations, interventions); + + cbase = cbase.getTwinVersion(); + + for (Proposition prop : cbase.getSignature()) { + result.add(prop); + result.add(new Negation(prop)); + } + for (CausalInterpretation model : models) { + for (Proposition prop : cbase.getSignature()) { + if (model.contains(prop)) { + if (!result.contains(prop) && result.contains(new Negation(prop))) { + result.remove(prop); + } + result.remove(new Negation(prop)); + } else { + if (result.contains(prop) && !result.contains(new Negation(prop))) { + result.remove(new Negation(prop)); + } + result.remove(prop); + } + } + } + return result; + } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java index 21000c2ef..f50f8bc24 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java @@ -112,6 +112,10 @@ public StructuralCausalModel getCausalModel() { return this.model.clone(); } + public CausalKnowledgeBase getTwinVersion() { + return new CausalKnowledgeBase(this.getCausalModel().getTwinModel(), this.getAssumptions()); + } + /** * Returns all propositional formulas of this knowledge base, i.e., * the structural equations of the corresponding causal model diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java index d5d37da92..957bdcd8b 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java @@ -210,7 +210,7 @@ public StructuralCausalModel getTwinModel() { assert model != null; // create twin copy for each structural equation - Map twinEquations = new HashMap<>(); + Map twinEquations = new HashMap<>(); for (PlFormula equation : structuralEquations) { PlFormula atom = ((Equivalence) equation).getFormulas().getFirst(); PlFormula cause = ((Equivalence) equation).getFormulas().getSecond(); @@ -219,7 +219,7 @@ public StructuralCausalModel getTwinModel() { PlFormula twinCause = cause.clone(); for (Proposition prop : cause.getAtoms()) { if (model.getBackgroundAtoms().contains(prop)) continue; - twinCause = twinCause.replace(prop, getCounterfactualCopy((Proposition) atom), cause.numberOfOccurrences(prop)); + twinCause = twinCause.replace(prop, getCounterfactualCopy(prop), twinCause.numberOfOccurrences(prop)); } twinEquations.put(twinAtom, twinCause); } else { @@ -228,16 +228,15 @@ public StructuralCausalModel getTwinModel() { } // add twin equations - while (!twinEquations.isEmpty()) { - for (PlFormula lit : twinEquations.keySet()) { - try { - this.addExplainableAtom(lit, twinEquations.get(lit)); - twinEquations.remove(lit); - } catch (CyclicDependencyException ignored) { - } + Queue remaining = new ArrayDeque<>(twinEquations.keySet()); + while (!remaining.isEmpty()) { + Proposition prop = remaining.poll(); + try { + model.addExplainableAtom(prop, twinEquations.get(prop)); + } catch (CyclicDependencyException ignored) { + remaining.add(prop); } } - return model; } @@ -281,7 +280,9 @@ public String toString() { @Override public StructuralCausalModel clone() { try { - return new StructuralCausalModel(this.getStructuralEquations()); + StructuralCausalModel model = new StructuralCausalModel(this.getStructuralEquations()); + model.addBackgroundAtoms(this.getBackgroundAtoms()); + return model; } catch (CyclicDependencyException ignored) { // will never happen } From da8ac4db8ca7b62ed3d6f131ee62627278ca5b20 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:39:18 +0100 Subject: [PATCH 26/43] update example --- .../examples/CounterfactualReasoningExample.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CounterfactualReasoningExample.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CounterfactualReasoningExample.java index 08e48a8f6..8499106d5 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CounterfactualReasoningExample.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CounterfactualReasoningExample.java @@ -86,7 +86,7 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend // Define variables for the example Collection observations = new HashSet<>(); - observations.add(shortOfBreath); + observations.add(fever); PlFormula conclusion1 = model.getCounterfactualCopy(fever); PlFormula conclusion2 = new Negation(model.getCounterfactualCopy(fever)); @@ -96,16 +96,17 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend // Example usage - DungTheory theory = reasoner.getInducedTheory(cbase,observations, interventions); - System.out.println("Induced Argumentation Framework:\n" + theory.prettyPrint()); - System.out.printf("Observing '%1$s' and intervening '%2$s' implies '%3$s': %4$s%n", observations, interventions, conclusion1, reasoner.query(cbase, observations, interventions, conclusion1)); System.out.printf("Observing '%1$s' and intervening '%2$s' implies '%3$s': %4$s%n", observations, interventions, conclusion2, reasoner.query(cbase, observations, interventions, conclusion2)); // modify observations observations.add(shortOfBreath); - System.out.printf("Observing '%1$s' and intervening '%2$s' implies '%3$s': %4$s%n", observations, interventions, conclusion1, reasoner.query(cbase, observations, interventions, conclusion1)); + System.out.printf("Observing '%1$s' and intervening '%2$s' implies '%3$s': %4$s%n", observations, interventions, conclusion2, reasoner.query(cbase, observations, interventions, conclusion2)); + DungTheory theory = reasoner.getInducedTheory(cbase.getTwinVersion(), observations, interventions); + System.out.println("Induced Argumentation Framework:\n" + theory.prettyPrint()); + System.out.println(reasoner.getModels(cbase, observations, interventions)); + System.out.println(reasoner.getConclusions(cbase, observations)); } } From 450c32947d59fad0810e0496d2a0978f7fdde59c Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:44:07 +0100 Subject: [PATCH 27/43] added initial version of parser and writer --- .../causal/parser/CausalParser.java | 21 +++++++++++++++++++ .../causal/writer/CausalWriter.java | 4 ++++ 2 files changed, 25 insertions(+) create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/parser/CausalParser.java create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/writer/CausalWriter.java diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/parser/CausalParser.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/parser/CausalParser.java new file mode 100644 index 000000000..ba350714e --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/parser/CausalParser.java @@ -0,0 +1,21 @@ +package org.tweetyproject.causal.parser; + +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.commons.Parser; +import org.tweetyproject.commons.ParserException; +import org.tweetyproject.logics.pl.syntax.PlFormula; + +import java.io.IOException; +import java.io.Reader; + +public class CausalParser extends Parser { + @Override + public CausalKnowledgeBase parseBeliefBase(Reader reader) throws IOException, ParserException { + return null; + } + + @Override + public PlFormula parseFormula(Reader reader) throws IOException, ParserException { + return null; + } +} diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/writer/CausalWriter.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/writer/CausalWriter.java new file mode 100644 index 000000000..69a82d296 --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/writer/CausalWriter.java @@ -0,0 +1,4 @@ +package org.tweetyproject.causal.writer; + +public class CausalWriter { +} From 2702c584114fde50ce40da9105371cc4ec871c0a Mon Sep 17 00:00:00 2001 From: Lars Bengel Date: Wed, 30 Oct 2024 14:08:13 +0100 Subject: [PATCH 28/43] minor fixes --- .../causal/parser/CausalParser.java | 21 +++++++++++++++++++ ...tractArgumentationBasedCausalReasoner.java | 18 ++++++++++++++++ ...umentationBasedCounterfactualReasoner.java | 1 - .../causal/writer/CausalWriter.java | 21 +++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/parser/CausalParser.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/parser/CausalParser.java index ba350714e..304e25c3e 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/parser/CausalParser.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/parser/CausalParser.java @@ -1,3 +1,21 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ package org.tweetyproject.causal.parser; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; @@ -8,6 +26,9 @@ import java.io.IOException; import java.io.Reader; +/** + * + */ public class CausalParser extends Parser { @Override public CausalKnowledgeBase parseBeliefBase(Reader reader) throws IOException, ParserException { diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java index 2e148fb8a..db9e00270 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/AbstractArgumentationBasedCausalReasoner.java @@ -1,3 +1,21 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ package org.tweetyproject.causal.reasoner; import org.tweetyproject.arg.dung.reasoner.AbstractExtensionReasoner; diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java index 478d4068b..f790c6693 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCounterfactualReasoner.java @@ -22,7 +22,6 @@ import org.tweetyproject.causal.semantics.CausalInterpretation; import org.tweetyproject.causal.semantics.CounterfactualStatement; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; -import org.tweetyproject.causal.syntax.StructuralCausalModel; import org.tweetyproject.logics.pl.syntax.Negation; import org.tweetyproject.logics.pl.syntax.PlFormula; import org.tweetyproject.logics.pl.syntax.Proposition; diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/writer/CausalWriter.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/writer/CausalWriter.java index 69a82d296..bc7cfa5c1 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/writer/CausalWriter.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/writer/CausalWriter.java @@ -1,4 +1,25 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ package org.tweetyproject.causal.writer; +/** + * + */ public class CausalWriter { } From f75231054e0f9e86bc8b3d4d39be939c1768c71b Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Sat, 16 Nov 2024 13:53:01 +0100 Subject: [PATCH 29/43] bugfix weighted argumentation --- .../java/org/tweetyproject/math/algebra/BottleneckSemiring.java | 2 +- .../java/org/tweetyproject/math/algebra/WeightedSemiring.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/org-tweetyproject-math/src/main/java/org/tweetyproject/math/algebra/BottleneckSemiring.java b/org-tweetyproject-math/src/main/java/org/tweetyproject/math/algebra/BottleneckSemiring.java index b65414dc4..caabde934 100644 --- a/org-tweetyproject-math/src/main/java/org/tweetyproject/math/algebra/BottleneckSemiring.java +++ b/org-tweetyproject-math/src/main/java/org/tweetyproject/math/algebra/BottleneckSemiring.java @@ -59,7 +59,7 @@ public Double validateAndReturn(Double value) { */ @Override public Double getRandomElement() { - return random.nextDouble(maxValue); + return random.nextDouble()*maxValue; } /** diff --git a/org-tweetyproject-math/src/main/java/org/tweetyproject/math/algebra/WeightedSemiring.java b/org-tweetyproject-math/src/main/java/org/tweetyproject/math/algebra/WeightedSemiring.java index 4809057fd..0f8f3d77a 100644 --- a/org-tweetyproject-math/src/main/java/org/tweetyproject/math/algebra/WeightedSemiring.java +++ b/org-tweetyproject-math/src/main/java/org/tweetyproject/math/algebra/WeightedSemiring.java @@ -71,7 +71,7 @@ public Double validateAndReturn(Double value) { */ @Override public Double getRandomElement() { - return random.nextDouble(maxValue); + return random.nextDouble()*maxValue; } /** From 9866d5b42ff59642f9c0b6614c410378baf2de69 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Fri, 24 Jan 2025 17:28:37 +0100 Subject: [PATCH 30/43] added some todos --- .../src/main/java/org/tweetyproject/causal/TODOS.txt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/TODOS.txt diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/TODOS.txt b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/TODOS.txt new file mode 100644 index 000000000..30a6595e8 --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/TODOS.txt @@ -0,0 +1,8 @@ +- implement that conclusion can be a formula instead of just a literal +- allow structural equations to be formulated for literals (ie allow "negative" causal relation) +- What if: Create arguments based on CKB alone (without observations) + arguments based on observations: Is this still correct? would create more interesting AFs + - Or probably better: argument premise must only be consistent with CM, not the observations + - Could also handle observations like assumptions (?) Probably not sensible + +- Parser +- Writer \ No newline at end of file From 8da55b5f30496d66bdbf5157081331a8066dac60 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Sun, 26 Jan 2025 11:37:10 +0100 Subject: [PATCH 31/43] added representations for explanations and reasoner --- org-tweetyproject-arg-explanations/pom.xml | 27 ++++++++ .../SufficientExplanationExample.java | 29 ++++++++ .../principles/ExplanationPrinciple.java | 4 ++ ...AbstractAcceptanceExplanationReasoner.java | 13 ++++ .../MinimalSequenceExplanationReasoner.java | 4 ++ .../MinimalSufficientExplanationReasoner.java | 4 ++ .../NecessaryExplanationReasoner.java | 4 ++ .../SequenceExplanationReasoner.java | 4 ++ .../StrongSequenceExplanationReasoner.java | 4 ++ .../SufficientExplanationReasoner.java | 69 +++++++++++++++++++ .../explanations/semantics/Explanation.java | 10 +++ .../semantics/SetExplanation.java | 4 ++ pom.xml | 1 + 13 files changed, 177 insertions(+) create mode 100644 org-tweetyproject-arg-explanations/pom.xml create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/examples/SufficientExplanationExample.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/principles/ExplanationPrinciple.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractAcceptanceExplanationReasoner.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSequenceExplanationReasoner.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/NecessaryExplanationReasoner.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SequenceExplanationReasoner.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/StrongSequenceExplanationReasoner.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SufficientExplanationReasoner.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/Explanation.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java diff --git a/org-tweetyproject-arg-explanations/pom.xml b/org-tweetyproject-arg-explanations/pom.xml new file mode 100644 index 000000000..ffcf0ad01 --- /dev/null +++ b/org-tweetyproject-arg-explanations/pom.xml @@ -0,0 +1,27 @@ + + 4.0.0 + + org.tweetyproject.arg + explanations + + TweetyProject (Explanations in Argumentation Library) + + + org.tweetyproject + parent-pom + 1.28-SNAPSHOT + .. + + + + org.tweetyproject.arg + dung + 1.28-SNAPSHOT + compile + + + diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/examples/SufficientExplanationExample.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/examples/SufficientExplanationExample.java new file mode 100644 index 000000000..ed707f19c --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/examples/SufficientExplanationExample.java @@ -0,0 +1,29 @@ +package org.tweetyproject.arg.explanations.examples; + +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.arg.explanations.reasoner.acceptance.SufficientExplanationReasoner; + +public class SufficientExplanationExample { + public static void main(String[] args) { + DungTheory theory = new DungTheory(); + Argument a = new Argument("a"); + Argument b = new Argument("b"); + Argument c = new Argument("c"); + Argument d = new Argument("d"); + Argument e = new Argument("e"); + theory.add(a,b,c,d,e); + theory.addAttack(a,b); + theory.addAttack(b,c); + theory.addAttack(b,d); + theory.addAttack(d,e); + + System.out.println(new SufficientExplanationReasoner().isRelevantFor(theory,a,c)); + System.out.println(new SufficientExplanationReasoner().isRelevantFor(theory,b,c)); + System.out.println(new SufficientExplanationReasoner().isRelevantFor(theory,c,c)); + System.out.println(new SufficientExplanationReasoner().isRelevantFor(theory,d,c)); + System.out.println(new SufficientExplanationReasoner().isRelevantFor(theory,e,c)); + + + } +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/principles/ExplanationPrinciple.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/principles/ExplanationPrinciple.java new file mode 100644 index 000000000..a2c42a7f5 --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/principles/ExplanationPrinciple.java @@ -0,0 +1,4 @@ +package org.tweetyproject.arg.explanations.principles; + +public class ExplanationPrinciple { +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractAcceptanceExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractAcceptanceExplanationReasoner.java new file mode 100644 index 000000000..062170c4f --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractAcceptanceExplanationReasoner.java @@ -0,0 +1,13 @@ +package org.tweetyproject.arg.explanations.reasoner.acceptance; + +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.arg.explanations.semantics.Explanation; + +import java.util.Collection; + +public abstract class AbstractAcceptanceExplanationReasoner { + public abstract Explanation getExplanation(DungTheory theory, Argument argument); + + public abstract Collection getExplanations(DungTheory theory, Argument argument); +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSequenceExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSequenceExplanationReasoner.java new file mode 100644 index 000000000..fa3165825 --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSequenceExplanationReasoner.java @@ -0,0 +1,4 @@ +package org.tweetyproject.arg.explanations.reasoner.acceptance; + +public class MinimalSequenceExplanationReasoner { +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java new file mode 100644 index 000000000..669344b25 --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java @@ -0,0 +1,4 @@ +package org.tweetyproject.arg.explanations.reasoner.acceptance; + +public class MinimalSufficientExplanationReasoner { +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/NecessaryExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/NecessaryExplanationReasoner.java new file mode 100644 index 000000000..0f0ff0c37 --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/NecessaryExplanationReasoner.java @@ -0,0 +1,4 @@ +package org.tweetyproject.arg.explanations.reasoner.acceptance; + +public class NecessaryExplanationReasoner { +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SequenceExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SequenceExplanationReasoner.java new file mode 100644 index 000000000..d23d23e1a --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SequenceExplanationReasoner.java @@ -0,0 +1,4 @@ +package org.tweetyproject.arg.explanations.reasoner.acceptance; + +public class SequenceExplanationReasoner { +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/StrongSequenceExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/StrongSequenceExplanationReasoner.java new file mode 100644 index 000000000..0f4df5bd9 --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/StrongSequenceExplanationReasoner.java @@ -0,0 +1,4 @@ +package org.tweetyproject.arg.explanations.reasoner.acceptance; + +public class StrongSequenceExplanationReasoner { +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SufficientExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SufficientExplanationReasoner.java new file mode 100644 index 000000000..39b6e8042 --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SufficientExplanationReasoner.java @@ -0,0 +1,69 @@ +package org.tweetyproject.arg.explanations.reasoner.acceptance; + +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.arg.explanations.semantics.Explanation; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Stack; + +public class SufficientExplanationReasoner extends AbstractAcceptanceExplanationReasoner { + + @Override + public Explanation getExplanation(DungTheory theory, Argument argument) { + return null; + } + + @Override + public Collection getExplanations(DungTheory theory, Argument argument) { + return null; + } + + /** + * Determines whether the argument 'a' is relevant for the argument 'b' in the given theory + * @param theory some argumentation framework + * @param a some argument + * @param b some argument + * @return TRUE iff 'a' is relevant for 'b' + */ + public boolean isRelevantFor(DungTheory theory, Argument a, Argument b) { + Collection relevant = getRelevantArguments(theory, b); + return relevant.contains(a); + } + + /** + * Determines whether the all arguments in the set are relevant for the argument 'b' in the given theory + * @param theory some argumentation framework + * @param arguments some set of arguments + * @param b some argument + * @return TRUE iff 'arguments' is relevant for 'b' + */ + public boolean isRelevantFor(DungTheory theory, Collection arguments, Argument b) { + Collection relevant = getRelevantArguments(theory, b); + return relevant.containsAll(arguments); + } + + /** + * + * @param theory + * @param argument + * @return + */ + public Collection getRelevantArguments(DungTheory theory, Argument argument) { + Collection result = new HashSet<>(); + Stack stack = new Stack<>(); + stack.push(argument); + + while (!stack.isEmpty()) { + Argument arg = stack.pop(); + if (result.contains(arg)) { + continue; + } + result.add(arg); + stack.addAll(theory.getAttackers(arg)); + } + + return result; + } +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/Explanation.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/Explanation.java new file mode 100644 index 000000000..3ed2ce7f7 --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/Explanation.java @@ -0,0 +1,10 @@ +package org.tweetyproject.arg.explanations.semantics; + +import org.tweetyproject.arg.dung.semantics.AbstractArgumentationInterpretation; +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; + +import java.util.Collection; + +public abstract class Explanation extends AbstractArgumentationInterpretation implements Collection,Comparable { +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java new file mode 100644 index 000000000..b63d79e9f --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java @@ -0,0 +1,4 @@ +package org.tweetyproject.arg.explanations.semantics; + +public class SetExplanation extends Explanation { +} diff --git a/pom.xml b/pom.xml index 05f86bb85..370b79387 100644 --- a/pom.xml +++ b/pom.xml @@ -225,6 +225,7 @@ org-tweetyproject-arg-caf org-tweetyproject-arg-deductive org-tweetyproject-arg-extended + org-tweetyproject-arg-explanations org-tweetyproject-arg-lp org-tweetyproject-arg-delp org-tweetyproject-arg-setaf From 9a7818e67831bb47a03949dfe6981c8dab3af6ef Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Tue, 11 Mar 2025 18:57:26 +0100 Subject: [PATCH 32/43] added new simple causal reasoner --- .../CausalReasoningExampleSurfer.java | 4 + .../causal/reasoner/SimpleCausalReasoner.java | 74 +++++++++++++++++++ .../semantics/CausalInterpretation.java | 28 ++++++- .../causal/syntax/CausalKnowledgeBase.java | 1 - .../causal/syntax/StructuralCausalModel.java | 4 +- 5 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/SimpleCausalReasoner.java diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java index 39f485075..205bbd358 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java @@ -19,7 +19,9 @@ package org.tweetyproject.causal.examples; import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.causal.reasoner.AbstractCausalReasoner; import org.tweetyproject.causal.reasoner.ArgumentationBasedCausalReasoner; +import org.tweetyproject.causal.reasoner.SimpleCausalReasoner; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.causal.syntax.StructuralCausalModel; import org.tweetyproject.logics.pl.syntax.*; @@ -94,5 +96,7 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend System.out.printf("Observing '%s' implies '%s': %s%n", observations, conclusion2, reasoner.query(cbase, observations, conclusion2)); System.out.printf("Possible Conclusions of observing '%1$s': %2$s%n", observations, reasoner.getConclusions(cbase, observations)); System.out.printf("Models: %s%n", reasoner.getModels(cbase, observations)); + + } } diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/SimpleCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/SimpleCausalReasoner.java new file mode 100644 index 000000000..bd00c062f --- /dev/null +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/SimpleCausalReasoner.java @@ -0,0 +1,74 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2024 The TweetyProject Team + */ +package org.tweetyproject.causal.reasoner; + +import org.tweetyproject.causal.semantics.CausalInterpretation; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.causal.syntax.StructuralCausalModel; +import org.tweetyproject.commons.InterpretationSet; +import org.tweetyproject.logics.commons.analysis.NaiveMusEnumerator; +import org.tweetyproject.logics.pl.sat.Sat4jSolver; +import org.tweetyproject.logics.pl.sat.SatSolver; +import org.tweetyproject.logics.pl.sat.SimpleModelEnumerator; +import org.tweetyproject.logics.pl.syntax.PlBeliefSet; +import org.tweetyproject.logics.pl.syntax.PlFormula; +import org.tweetyproject.logics.pl.syntax.Proposition; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; + +/** + * Naive reasoner for causal reasoning + * + * @author Lars Bengel + */ +public class SimpleCausalReasoner extends AbstractCausalReasoner { + @Override + public Collection getModels(CausalKnowledgeBase cbase, Collection observations, Map interventions) { + // Perform interventions + StructuralCausalModel model = cbase.getCausalModel(); + for (Proposition atom : interventions.keySet()) { + model = model.intervene(atom, interventions.get(atom)); + } + PlBeliefSet knowledge = new PlBeliefSet(model.getStructuralEquations()); + knowledge.addAll(observations); + + // Enumerate through all maximally consistent subsets of the assumptions and compute the models for each set + knowledge + SatSolver solver = new Sat4jSolver(); + NaiveMusEnumerator enumerator = new NaiveMusEnumerator<>(solver); + + SimpleModelEnumerator modelEnumerator = new SimpleModelEnumerator(); + + Collection> maximalConsistentSubsets = enumerator.maximalConsistentSubsets(cbase.getAssumptions()); + + Collection result = new HashSet<>(); + for (Collection set : maximalConsistentSubsets) { + PlBeliefSet set2 = new PlBeliefSet(knowledge); + set2.addAll(set); + for (InterpretationSet itp : modelEnumerator.getModels(set2)) { + CausalInterpretation interpretation = new CausalInterpretation(); + interpretation.addAll(itp); + result.add(interpretation); + } + } + + return result; + } +} diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java index 42dbb49de..8a55ae15e 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/semantics/CausalInterpretation.java @@ -20,8 +20,7 @@ import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.commons.InterpretationSet; -import org.tweetyproject.logics.pl.syntax.PlFormula; -import org.tweetyproject.logics.pl.syntax.Proposition; +import org.tweetyproject.logics.pl.syntax.*; import java.util.Collection; @@ -29,7 +28,6 @@ * Representation of a propositional interpretation of a causal knowledge base * * @author Lars Bengel - * TODO add explicit encoding of rejected atoms */ public class CausalInterpretation extends InterpretationSet { @@ -50,7 +48,29 @@ public CausalInterpretation(Collection formulas) { @Override public boolean satisfies(PlFormula formula) throws IllegalArgumentException { - return this.contains(formula); + if(formula instanceof Contradiction) + return false; + if(formula instanceof Tautology) + return true; + if(formula instanceof Proposition) + return this.contains(formula); + if(formula instanceof Negation) + return !this.satisfies(((Negation)formula).getFormula()); + if(formula instanceof Conjunction){ + Conjunction c = (Conjunction) formula; + for(PlFormula f : c) + if(!this.satisfies(f)) + return false; + return true; + } + if(formula instanceof Disjunction){ + Disjunction d = (Disjunction) formula; + for(PlFormula f: d) + if(this.satisfies(f)) + return true; + return false; + } + throw new IllegalArgumentException("Propositional formula " + formula + " is of unsupported type."); } @Override diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java index f50f8bc24..b1678b425 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/CausalKnowledgeBase.java @@ -18,7 +18,6 @@ */ package org.tweetyproject.causal.syntax; -import org.tweetyproject.logics.pl.reasoner.SimplePlReasoner; import org.tweetyproject.logics.pl.syntax.Negation; import org.tweetyproject.logics.pl.syntax.PlBeliefSet; import org.tweetyproject.logics.pl.syntax.PlFormula; diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java index 957bdcd8b..7382ed35b 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java @@ -31,8 +31,6 @@ * * @author Julian Sander * @author Lars Bengel - * TODO explainableAtoms needs to store somehow whether the atom is to be negated in the equation - * TODO or the equation must be transformed before storing */ public class StructuralCausalModel implements BeliefBase, Collection { /** The background atoms of the causal model */ @@ -66,7 +64,7 @@ public StructuralCausalModel(Collection equations) throws CyclicDepen if (formula instanceof Equivalence) { Equivalence equation = (Equivalence) formula; PlFormula left = equation.getFormulas().getFirst(); - if (!(left instanceof Proposition)) { // TODO allow literal here + if (!(left instanceof Proposition)) { throw new IllegalArgumentException("Left side of Structural Equation must be literal!"); } explainable.addAll(left.getAtoms()); From cb36f7de5fc4faadd6491d6ba39fce1a536ef568 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Sun, 16 Mar 2025 18:54:27 +0100 Subject: [PATCH 33/43] todos --- .../src/main/java/org/tweetyproject/causal/TODOS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/TODOS.txt b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/TODOS.txt index 30a6595e8..baf824eb0 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/TODOS.txt +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/TODOS.txt @@ -3,6 +3,7 @@ - What if: Create arguments based on CKB alone (without observations) + arguments based on observations: Is this still correct? would create more interesting AFs - Or probably better: argument premise must only be consistent with CM, not the observations - Could also handle observations like assumptions (?) Probably not sensible +- add explicit encoding of negative atoms in causalInterpretation - Parser - Writer \ No newline at end of file From 4720a4c46ebe6b4cf6fb553156cb4dd2ca6785ac Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:14:51 +0100 Subject: [PATCH 34/43] minor --- .../causal/examples/CausalReasoningExampleSurfer.java | 2 ++ .../causal/examples/CausalReasoningExampleVirus.java | 3 +++ 2 files changed, 5 insertions(+) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java index 205bbd358..e9cc7d034 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java @@ -75,6 +75,8 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend cbase.addAssumption(giantWave); cbase.addAssumption(jellyfish); + //System.out.println(new SimpleCausalReasoner().getConclusions(cbase, new Negation(drowning))); + System.out.println("Causal Knowledge Base: " + cbase); // Define variables for the example diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java index b0e06af15..498cdfdca 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java @@ -21,6 +21,7 @@ import org.tweetyproject.arg.dung.syntax.DungTheory; import org.tweetyproject.arg.dung.util.DungTheoryPlotter; import org.tweetyproject.causal.reasoner.ArgumentationBasedCausalReasoner; +import org.tweetyproject.causal.reasoner.SimpleCausalReasoner; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.causal.syntax.StructuralCausalModel; import org.tweetyproject.logics.pl.syntax.*; @@ -74,9 +75,11 @@ public static void main(String[] args) throws StructuralCausalModel.CyclicDepend cbase.addAssumption(new Negation(corona)); cbase.addAssumption(new Negation(influenza)); cbase.addAssumption(new Negation(atRisk)); + //cbase.addAssumption(corona); cbase.addAssumption(atRisk); //cbase.addAssumption(influenza); + System.out.println("Causal Knowledge Base: " + cbase); From 903716b4e36baf5b1b795449d401f80ab4d00302 Mon Sep 17 00:00:00 2001 From: larsbengel <77848869+larsbengel@users.noreply.github.com> Date: Thu, 27 Mar 2025 16:24:44 +0100 Subject: [PATCH 35/43] bugfix StructuralCausalModel --- .../org/tweetyproject/causal/syntax/StructuralCausalModel.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java index 7382ed35b..a5d74a8e0 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/syntax/StructuralCausalModel.java @@ -79,7 +79,10 @@ public StructuralCausalModel(Collection equations) throws CyclicDepen // construct new model from equations this.addBackgroundAtoms(atoms); Queue remaining = new ArrayDeque<>(explainable); + int bound = remaining.size() * remaining.size(); + int i = 0; while (!remaining.isEmpty()) { + if (i++ > bound) throw new CyclicDependencyException("Equations contain cyclic dependency"); Proposition prop = remaining.poll(); try { this.addExplainableAtom(prop, formulas.get(prop)); From bb877f2bb6216a54700b39983f0ae980b4929fa1 Mon Sep 17 00:00:00 2001 From: Oleksandr Dzhychko Date: Thu, 20 Mar 2025 15:03:57 +0100 Subject: [PATCH 36/43] test(causal): add unit test for causal reasoners The unit test shows an issue with `SimpleCausalReasoner`. --- .../AbstractCausalReasonerTestBase.java | 75 +++++++++++++++++++ .../ArgumentationBasedCausalReasonerTest.java | 27 +++++++ .../reasoner/SimpleCausalReasonerTest.java | 27 +++++++ 3 files changed, 129 insertions(+) create mode 100644 org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/AbstractCausalReasonerTestBase.java create mode 100644 org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasonerTest.java create mode 100644 org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java diff --git a/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/AbstractCausalReasonerTestBase.java b/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/AbstractCausalReasonerTestBase.java new file mode 100644 index 000000000..459a9d617 --- /dev/null +++ b/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/AbstractCausalReasonerTestBase.java @@ -0,0 +1,75 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2025 The TweetyProject Team + */ +package org.tweetyproject.causal.reasoner; + +import org.junit.jupiter.api.Test; +import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +import org.tweetyproject.causal.syntax.StructuralCausalModel; +import org.tweetyproject.logics.pl.syntax.Equivalence; +import org.tweetyproject.logics.pl.syntax.Negation; +import org.tweetyproject.logics.pl.syntax.PlFormula; +import org.tweetyproject.logics.pl.syntax.Proposition; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public abstract class AbstractCausalReasonerTestBase { + protected abstract Reasoner createReasoner(); + + @Test + public void example1() throws Throwable { + Proposition corona = new Proposition("corona"); + Proposition influenza = new Proposition("influenza"); + Proposition atRisk = new Proposition("at-risk"); + Proposition covid = new Proposition("covid"); + Proposition flu = new Proposition("flu"); + Proposition shortOfBreath = new Proposition("short-of-breath"); + Proposition fever = new Proposition("fever"); + Proposition chills = new Proposition("chills"); + Collection modelEquations = List.of( + new Equivalence(covid, corona), + new Equivalence(flu, influenza), + new Equivalence(fever, covid.combineWithOr(flu)), + new Equivalence(chills, fever), + new Equivalence(shortOfBreath, covid.combineWithAnd(atRisk)) + ); + StructuralCausalModel model = new StructuralCausalModel(modelEquations); + List assumptions = List.of(atRisk, corona, new Negation(influenza)); + CausalKnowledgeBase knowledgeBase = new CausalKnowledgeBase(model, assumptions); + Collection observations = List.of(covid); + AbstractCausalReasoner reasoner = createReasoner(); + + Collection conclusions = reasoner.getConclusions(knowledgeBase, observations); + + Collection expectedConclusions = Set.of( + atRisk, + corona, + new Negation(influenza), + covid, + shortOfBreath, + fever, + chills, + new Negation(flu) + ); + assertEquals(expectedConclusions, conclusions); + } +} diff --git a/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasonerTest.java b/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasonerTest.java new file mode 100644 index 000000000..8c31ae5bf --- /dev/null +++ b/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/ArgumentationBasedCausalReasonerTest.java @@ -0,0 +1,27 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2025 The TweetyProject Team + */ +package org.tweetyproject.causal.reasoner; + +public class ArgumentationBasedCausalReasonerTest extends AbstractCausalReasonerTestBase { + + @Override + protected ArgumentationBasedCausalReasoner createReasoner() { + return new ArgumentationBasedCausalReasoner(); + } +} \ No newline at end of file diff --git a/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java b/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java new file mode 100644 index 000000000..eef05dd09 --- /dev/null +++ b/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java @@ -0,0 +1,27 @@ +/* + * This file is part of "TweetyProject", a collection of Java libraries for + * logical aspects of artificial intelligence and knowledge representation. + * + * TweetyProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Copyright 2025 The TweetyProject Team + */ +package org.tweetyproject.causal.reasoner; + +public class SimpleCausalReasonerTest extends AbstractCausalReasonerTestBase { + + @Override + protected SimpleCausalReasoner createReasoner() { + return new SimpleCausalReasoner(); + } +} \ No newline at end of file From 8bf6af3adcb7738525292768a29771912984b9b3 Mon Sep 17 00:00:00 2001 From: Lars Bengel Date: Fri, 4 Apr 2025 17:19:36 +0200 Subject: [PATCH 37/43] minor changes --- .../principles/ExplanationPrinciple.java | 30 +++++- ...AbstractAcceptanceExplanationReasoner.java | 4 +- .../MinimalSufficientExplanationReasoner.java | 27 +++++- .../NecessaryExplanationReasoner.java | 22 ++++- .../StrongSigmaExplanationReasoner.java | 4 + .../SufficientExplanationReasoner.java | 17 ++-- .../semantics/SetExplanation.java | 97 +++++++++++++++++++ 7 files changed, 190 insertions(+), 11 deletions(-) create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/StrongSigmaExplanationReasoner.java diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/principles/ExplanationPrinciple.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/principles/ExplanationPrinciple.java index a2c42a7f5..069036f29 100644 --- a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/principles/ExplanationPrinciple.java +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/principles/ExplanationPrinciple.java @@ -1,4 +1,32 @@ package org.tweetyproject.arg.explanations.principles; -public class ExplanationPrinciple { +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.arg.explanations.reasoner.acceptance.AbstractAcceptanceExplanationReasoner; +import org.tweetyproject.commons.postulates.Postulate; +import org.tweetyproject.commons.postulates.PostulateEvaluatable; + +import java.util.Collection; + +public abstract class ExplanationPrinciple implements Postulate { + @Override + public boolean isApplicable(Collection kb) { + return kb instanceof DungTheory; + } + + @Override + public boolean isSatisfied(Collection kb, PostulateEvaluatable ev) { + if(ev instanceof AbstractAcceptanceExplanationReasoner) + return this.isSatisfied(kb, (AbstractAcceptanceExplanationReasoner) ev); + throw new RuntimeException("PostulateEvaluatable of type AbstractAcceptanceExplanationReasoner expected."); + } + + /** + * Determines whether this principle is satisfied by the given reasoner for the given instance + * @param kb some knowledge base + * @param ev some reasoner + * @return TRUE, iff this principle is satisfied by the given reasoner for the given instance + */ + public abstract boolean isSatisfied(Collection kb, AbstractAcceptanceExplanationReasoner ev); + } diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractAcceptanceExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractAcceptanceExplanationReasoner.java index 062170c4f..32b5628d2 100644 --- a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractAcceptanceExplanationReasoner.java +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractAcceptanceExplanationReasoner.java @@ -7,7 +7,9 @@ import java.util.Collection; public abstract class AbstractAcceptanceExplanationReasoner { - public abstract Explanation getExplanation(DungTheory theory, Argument argument); + public Explanation getExplanation(DungTheory theory, Argument argument) { + return getExplanations(theory, argument).iterator().next(); + } public abstract Collection getExplanations(DungTheory theory, Argument argument); } diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java index 669344b25..f155c1695 100644 --- a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java @@ -1,4 +1,29 @@ package org.tweetyproject.arg.explanations.reasoner.acceptance; -public class MinimalSufficientExplanationReasoner { +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.arg.explanations.semantics.Explanation; + +import java.util.Collection; +import java.util.HashSet; + +public class MinimalSufficientExplanationReasoner extends AbstractAcceptanceExplanationReasoner { + @Override + public Collection getExplanations(DungTheory theory, Argument argument) { + Collection result = new HashSet<>(); + Collection sufficientExplanations = new SufficientExplanationReasoner().getExplanations(theory, argument); + for (Explanation expl1 : sufficientExplanations) { + boolean minimal = true; + for (Explanation expl2 : sufficientExplanations) { + if (expl1.containsAll(expl2) && !expl2.containsAll(expl1)) { + minimal = false; + break; + } + } + if (minimal) { + result.add(expl1); + } + } + return result; + } } diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/NecessaryExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/NecessaryExplanationReasoner.java index 0f0ff0c37..b6257d3d8 100644 --- a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/NecessaryExplanationReasoner.java +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/NecessaryExplanationReasoner.java @@ -1,4 +1,24 @@ package org.tweetyproject.arg.explanations.reasoner.acceptance; -public class NecessaryExplanationReasoner { +import org.tweetyproject.arg.dung.reasoner.SimpleAdmissibleReasoner; +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.arg.explanations.semantics.Explanation; +import org.tweetyproject.arg.explanations.semantics.SetExplanation; + +import java.util.Collection; +import java.util.HashSet; + +public class NecessaryExplanationReasoner extends AbstractAcceptanceExplanationReasoner { + @Override + public Collection getExplanations(DungTheory theory, Argument argument) { + Collection result = new HashSet<>(); + Collection intersection = new HashSet<>(theory); + for (Collection arguments : new SimpleAdmissibleReasoner().getModels(theory)) { + if (!arguments.contains(argument)) continue; + intersection.retainAll(arguments); + } + result.add(new SetExplanation(intersection)); + return result; + } } diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/StrongSigmaExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/StrongSigmaExplanationReasoner.java new file mode 100644 index 000000000..501477419 --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/StrongSigmaExplanationReasoner.java @@ -0,0 +1,4 @@ +package org.tweetyproject.arg.explanations.reasoner.acceptance; + +public class StrongSigmaExplanationReasoner { +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SufficientExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SufficientExplanationReasoner.java index 39b6e8042..a5c9e073e 100644 --- a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SufficientExplanationReasoner.java +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SufficientExplanationReasoner.java @@ -1,23 +1,26 @@ package org.tweetyproject.arg.explanations.reasoner.acceptance; +import org.tweetyproject.arg.dung.reasoner.SimpleAdmissibleReasoner; +import org.tweetyproject.arg.dung.semantics.Extension; import org.tweetyproject.arg.dung.syntax.Argument; import org.tweetyproject.arg.dung.syntax.DungTheory; import org.tweetyproject.arg.explanations.semantics.Explanation; +import org.tweetyproject.arg.explanations.semantics.SetExplanation; import java.util.Collection; import java.util.HashSet; import java.util.Stack; public class SufficientExplanationReasoner extends AbstractAcceptanceExplanationReasoner { - - @Override - public Explanation getExplanation(DungTheory theory, Argument argument) { - return null; - } - @Override public Collection getExplanations(DungTheory theory, Argument argument) { - return null; + Collection result = new HashSet<>(); + for (Extension admSet : new SimpleAdmissibleReasoner().getModels(theory)) { + if (!admSet.contains(argument)) continue; + if (!isRelevantFor(theory, admSet, argument)) continue; + result.add(new SetExplanation(admSet)); + } + return result; } /** diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java index b63d79e9f..b171d4d6d 100644 --- a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java @@ -1,4 +1,101 @@ package org.tweetyproject.arg.explanations.semantics; +import org.tweetyproject.arg.dung.semantics.ArgumentStatus; +import org.tweetyproject.arg.dung.semantics.Extension; +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; + +import java.util.Collection; +import java.util.Iterator; + public class SetExplanation extends Explanation { + + protected Collection arguments; + + public SetExplanation(Collection arguments) { + this.arguments = arguments; + } + + @Override + public int compareTo(Explanation o) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + @Override + public int size() { + return this.arguments.size(); + } + + @Override + public boolean isEmpty() { + return this.arguments.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return this.arguments.contains(o); + } + + @Override + public Iterator iterator() { + return this.arguments.iterator(); + } + + @Override + public Object[] toArray() { + return this.arguments.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return this.arguments.toArray(a); + } + + @Override + public boolean add(Argument argument) { + return this.arguments.add(argument); + } + + @Override + public boolean remove(Object o) { + return this.arguments.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return this.arguments.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + return this.arguments.addAll(c); + } + + @Override + public boolean removeAll(Collection c) { + return this.arguments.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return this.arguments.retainAll(c); + } + + @Override + public void clear() { + this.arguments.clear(); + } + + @Override + public Extension getArgumentsOfStatus(ArgumentStatus status) { + return switch (status) { + case IN -> new Extension<>(arguments); + default -> new Extension<>(); + }; + } + + @Override + public String toString() { + return new Extension(arguments).toString(); + } } From 637cc0d0dab7bea9da62618577be473fa4fad1c8 Mon Sep 17 00:00:00 2001 From: Lars Bengel Date: Mon, 15 Sep 2025 11:59:13 +0200 Subject: [PATCH 38/43] added reasoners for sequence explanations --- .../examples/SequenceExplanationExample.java | 41 +++++ .../AbstractSequenceExplanationReasoner.java | 6 + ...ialecticalSequenceExplanationReasoner.java | 39 +++++ .../MinimalSequenceExplanationReasoner.java | 28 ++- .../MinimalSufficientExplanationReasoner.java | 17 +- .../SequenceExplanationReasoner.java | 58 ++++++- .../DialectialSequenceExplanation.java | 60 +++++++ .../explanations/semantics/Explanation.java | 2 +- .../semantics/SequenceExplanation.java | 161 ++++++++++++++++++ .../semantics/SetExplanation.java | 2 +- 10 files changed, 395 insertions(+), 19 deletions(-) create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/examples/SequenceExplanationExample.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractSequenceExplanationReasoner.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/DialecticalSequenceExplanationReasoner.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/DialectialSequenceExplanation.java create mode 100644 org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SequenceExplanation.java diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/examples/SequenceExplanationExample.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/examples/SequenceExplanationExample.java new file mode 100644 index 000000000..476bf95d3 --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/examples/SequenceExplanationExample.java @@ -0,0 +1,41 @@ +package org.tweetyproject.arg.explanations.examples; + +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.arg.explanations.reasoner.acceptance.AbstractSequenceExplanationReasoner; +import org.tweetyproject.arg.explanations.reasoner.acceptance.DialecticalSequenceExplanationReasoner; +import org.tweetyproject.arg.explanations.reasoner.acceptance.MinimalSequenceExplanationReasoner; +import org.tweetyproject.arg.explanations.reasoner.acceptance.SequenceExplanationReasoner; +import org.tweetyproject.arg.explanations.semantics.Explanation; + +import java.util.Collection; + +public class SequenceExplanationExample { + public static void main(String[] args) { + AbstractSequenceExplanationReasoner reasoner = new DialecticalSequenceExplanationReasoner(); + DungTheory theory = new DungTheory(); + Argument a = new Argument("a"); + Argument b = new Argument("b"); + Argument c = new Argument("c"); + Argument d = new Argument("d"); + Argument e = new Argument("e"); + Argument f = new Argument("f"); + Argument g = new Argument("g"); + Argument h = new Argument("h"); + theory.add(a,b,c,d,e,f,g,h); + theory.addAttack(f,c); + theory.addAttack(f,h); + theory.addAttack(c,b); + theory.addAttack(b,h); + theory.addAttack(b,a); + theory.addAttack(h,g); + theory.addAttack(a,g); + theory.addAttack(g,a); + theory.addAttack(e,a); + theory.addAttack(d,e); + theory.addAttack(e,d); + + Collection explanations = reasoner.getExplanations(theory, g); + System.out.println(explanations); + } +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractSequenceExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractSequenceExplanationReasoner.java new file mode 100644 index 000000000..909b87deb --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/AbstractSequenceExplanationReasoner.java @@ -0,0 +1,6 @@ +package org.tweetyproject.arg.explanations.reasoner.acceptance; + +import org.tweetyproject.arg.explanations.semantics.Explanation; + +public abstract class AbstractSequenceExplanationReasoner extends AbstractAcceptanceExplanationReasoner { +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/DialecticalSequenceExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/DialecticalSequenceExplanationReasoner.java new file mode 100644 index 000000000..0b619262e --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/DialecticalSequenceExplanationReasoner.java @@ -0,0 +1,39 @@ +package org.tweetyproject.arg.explanations.reasoner.acceptance; + +import org.tweetyproject.arg.dung.serialisability.semantics.SerialisationSequence; +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.arg.explanations.semantics.DialectialSequenceExplanation; +import org.tweetyproject.arg.explanations.semantics.Explanation; +import org.tweetyproject.arg.explanations.semantics.SequenceExplanation; + +import java.util.Collection; +import java.util.HashSet; + +public class DialecticalSequenceExplanationReasoner extends AbstractSequenceExplanationReasoner { + @Override + public Collection getExplanations(DungTheory theory, Argument argument) { + Collection explanations = new MinimalSequenceExplanationReasoner().getExplanations(theory, argument); + Collection result = new HashSet<>(); + for (Explanation e : explanations) { + SerialisationSequence seq = (SerialisationSequence) ((SequenceExplanation) e).getSequence(); + DialectialSequenceExplanation ex = new DialectialSequenceExplanation(argument); + for (Collection s : seq) { + Collection t = getDefeated(theory, seq, argument, (Collection) s); + ex.add((Collection) s, t); + } + result.add(ex); + } + return result; + } + + private Collection getDefeated(DungTheory theory, SerialisationSequence seq, Argument a, Collection s) { + Collection relevantAttackers = new HashSet<>(); + relevantAttackers.addAll(theory.getAttackers(a)); + relevantAttackers.addAll(seq.getExtension()); + DungTheory reduct = theory.getReduct(new SerialisationSequence(seq.subList(0, seq.indexOf(s))).getExtension()); + Collection attacked = new HashSet<>(reduct.getAttacked(s)); + relevantAttackers.retainAll(attacked); + return relevantAttackers; + } +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSequenceExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSequenceExplanationReasoner.java index fa3165825..f5abf6f98 100644 --- a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSequenceExplanationReasoner.java +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSequenceExplanationReasoner.java @@ -1,4 +1,30 @@ package org.tweetyproject.arg.explanations.reasoner.acceptance; -public class MinimalSequenceExplanationReasoner { +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.arg.explanations.semantics.Explanation; +import org.tweetyproject.arg.explanations.semantics.SequenceExplanation; + +import java.util.Collection; +import java.util.HashSet; + +public class MinimalSequenceExplanationReasoner extends AbstractSequenceExplanationReasoner { + @Override + public Collection getExplanations(DungTheory theory, Argument argument) { + Collection explanations = new SequenceExplanationReasoner().getExplanations(theory, argument); + int minLength = theory.size(); + for (Explanation explanation : explanations) { + if (((SequenceExplanation) explanation).size() < minLength) { + minLength = ((SequenceExplanation) explanation).size(); + } + } + + Collection result = new HashSet<>(); + for (Explanation explanation : explanations) { + if (((SequenceExplanation) explanation).size() == minLength) { + result.add(explanation); + } + } + return result; + } } diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java index f155c1695..aff82c6e7 100644 --- a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/MinimalSufficientExplanationReasoner.java @@ -10,20 +10,7 @@ public class MinimalSufficientExplanationReasoner extends AbstractAcceptanceExplanationReasoner { @Override public Collection getExplanations(DungTheory theory, Argument argument) { - Collection result = new HashSet<>(); - Collection sufficientExplanations = new SufficientExplanationReasoner().getExplanations(theory, argument); - for (Explanation expl1 : sufficientExplanations) { - boolean minimal = true; - for (Explanation expl2 : sufficientExplanations) { - if (expl1.containsAll(expl2) && !expl2.containsAll(expl1)) { - minimal = false; - break; - } - } - if (minimal) { - result.add(expl1); - } - } - return result; + // TODO + return null; } } diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SequenceExplanationReasoner.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SequenceExplanationReasoner.java index d23d23e1a..f1f3eabe1 100644 --- a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SequenceExplanationReasoner.java +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/reasoner/acceptance/SequenceExplanationReasoner.java @@ -1,4 +1,60 @@ package org.tweetyproject.arg.explanations.reasoner.acceptance; -public class SequenceExplanationReasoner { +import org.tweetyproject.arg.dung.reasoner.SerialisedExtensionReasoner; +import org.tweetyproject.arg.dung.reasoner.SimpleInitialReasoner; +import org.tweetyproject.arg.dung.semantics.Extension; +import org.tweetyproject.arg.dung.semantics.Semantics; +import org.tweetyproject.arg.dung.serialisability.semantics.SerialisationSequence; +import org.tweetyproject.arg.dung.serialisability.syntax.SelectionFunction; +import org.tweetyproject.arg.dung.syntax.Argument; +import org.tweetyproject.arg.dung.syntax.DungTheory; +import org.tweetyproject.arg.explanations.semantics.Explanation; +import org.tweetyproject.arg.explanations.semantics.SequenceExplanation; + +import java.util.*; + +import static org.tweetyproject.arg.dung.reasoner.SimpleInitialReasoner.Initial.*; + +public class SequenceExplanationReasoner extends AbstractSequenceExplanationReasoner { + @Override + public Collection getExplanations(DungTheory theory, Argument argument) { + Collection sequences = getExplanations(theory, argument, new SerialisationSequence()); + Collection result = new HashSet<>(); + for (SerialisationSequence seq : sequences) { + result.add(new SequenceExplanation(argument, seq)); + } + return result; + } + + /** + * Recursively computes all serialisation sequences wrt. the semantics + * + * @param theory an argumentation framework + * @param parentSequence the current serialisation sequence + * @return the set of serialisation sequences + */ + private Collection getExplanations(DungTheory theory, Argument argument, SerialisationSequence parentSequence) { + Collection result = new HashSet<>(); + + // check whether the current state is acceptable, if yes add to results + if (!parentSequence.isEmpty() && parentSequence.get(parentSequence.size()-1).contains(argument)) { + result.add(parentSequence); + return result; + } + Map>> initialSets = SimpleInitialReasoner + .partitionInitialSets(theory); + Collection> candidateSets = SelectionFunction.ADMISSIBLE.execute((Set>) initialSets.get(UA), (Set>) initialSets.get(UC), + (Set>) initialSets.get(C)); + // iterate depth-first through all initial sets (and hence their induced states) + // and add all found serialisation sequences + for (Extension set : candidateSets) { + DungTheory reduct = theory.getReduct(set); + + SerialisationSequence newSequence = new SerialisationSequence(parentSequence); + newSequence.add(set); + // continue computation of the serialisation in the reduct + result.addAll(this.getExplanations(reduct, argument, newSequence)); + } + return result; + } } diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/DialectialSequenceExplanation.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/DialectialSequenceExplanation.java new file mode 100644 index 000000000..4fdc68f2e --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/DialectialSequenceExplanation.java @@ -0,0 +1,60 @@ +package org.tweetyproject.arg.explanations.semantics; + +import org.tweetyproject.arg.dung.semantics.ArgumentStatus; +import org.tweetyproject.arg.dung.semantics.Extension; +import org.tweetyproject.arg.dung.syntax.Argument; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class DialectialSequenceExplanation extends Explanation { + private Argument argument; + private List> supporters; + private List> defeated; + + public DialectialSequenceExplanation(Argument argument) { + this.argument = argument; + this.supporters = new ArrayList<>(); + this.defeated = new ArrayList<>(); + } + + public boolean add(Collection s, Collection t) { + boolean result = false; + result |= supporters.add(s); + result |= defeated.add(t); + return result; + } + + public Argument getArgument() { + return argument; + } + + public List> getDefeated() { + return defeated; + } + + public List> getSupporters() { + return supporters; + } + + @Override + public int compareTo(Explanation o) { + return 0; + } + + @Override + public Extension getArgumentsOfStatus(ArgumentStatus status) { + return null; + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < supporters.size(); i++) { + if (i!=0) s.append(", "); + s.append(String.format("[%s, %s]", supporters.get(i), defeated.get(i))); + } + return s.toString(); + } +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/Explanation.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/Explanation.java index 3ed2ce7f7..6beee6f5c 100644 --- a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/Explanation.java +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/Explanation.java @@ -6,5 +6,5 @@ import java.util.Collection; -public abstract class Explanation extends AbstractArgumentationInterpretation implements Collection,Comparable { +public abstract class Explanation extends AbstractArgumentationInterpretation implements Comparable { } diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SequenceExplanation.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SequenceExplanation.java new file mode 100644 index 000000000..9808f35e5 --- /dev/null +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SequenceExplanation.java @@ -0,0 +1,161 @@ +package org.tweetyproject.arg.explanations.semantics; + +import org.tweetyproject.arg.dung.semantics.ArgumentStatus; +import org.tweetyproject.arg.dung.semantics.Extension; +import org.tweetyproject.arg.dung.serialisability.semantics.SerialisationSequence; +import org.tweetyproject.arg.dung.syntax.Argument; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +public class SequenceExplanation extends Explanation implements List> { + + List> sequence; + Argument argument; + + public SequenceExplanation(Argument argument, SerialisationSequence sequence) { + this.argument = argument; + this.sequence = sequence; + } + + public List> getSequence() { + return new SerialisationSequence(sequence); + } + + public Argument getArgument() { + return argument; + } + + @Override + public int compareTo(Explanation o) { + // TODO + return 0; + } + + @Override + public Extension getArgumentsOfStatus(ArgumentStatus status) { + return ((SerialisationSequence) sequence).getArgumentsOfStatus(status); + } + + @Override + public String toString() { + return String.format("%s: %s", getArgument(), sequence.toString()); + } + + @Override + public int size() { + return sequence.size(); + } + + @Override + public boolean isEmpty() { + return sequence.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return sequence.contains(o); + } + + @Override + public Iterator> iterator() { + return sequence.iterator(); + } + + @Override + public Object[] toArray() { + return sequence.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return sequence.toArray(a); + } + + @Override + public boolean add(Collection arguments) { + return sequence.add(arguments); + } + + @Override + public boolean remove(Object o) { + return sequence.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return sequence.containsAll(c); + } + + @Override + public boolean addAll(Collection> c) { + return sequence.addAll(c); + } + + @Override + public boolean addAll(int index, Collection> c) { + return sequence.addAll(index, c); + } + + @Override + public boolean removeAll(Collection c) { + return sequence.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return sequence.retainAll(c); + } + + @Override + public void clear() { + sequence.clear(); + } + + @Override + public Collection get(int index) { + return sequence.get(index); + } + + @Override + public Collection set(int index, Collection element) { + return sequence.set(index, element); + } + + @Override + public void add(int index, Collection element) { + sequence.add(index, element); + } + + @Override + public Collection remove(int index) { + return sequence.remove(index); + } + + @Override + public int indexOf(Object o) { + return sequence.indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + return sequence.lastIndexOf(o); + } + + @Override + public ListIterator> listIterator() { + return sequence.listIterator(); + } + + @Override + public ListIterator> listIterator(int index) { + return sequence.listIterator(index); + } + + @Override + public List> subList(int fromIndex, int toIndex) { + return sequence.subList(fromIndex, toIndex); + } +} diff --git a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java index b171d4d6d..ec58c81d4 100644 --- a/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java +++ b/org-tweetyproject-arg-explanations/src/main/java/org/tweetyproject/arg/explanations/semantics/SetExplanation.java @@ -8,7 +8,7 @@ import java.util.Collection; import java.util.Iterator; -public class SetExplanation extends Explanation { +public class SetExplanation extends Explanation implements Collection { protected Collection arguments; From 39cff2a2eb16b0a3a1296916c74d5dda4a4e4db6 Mon Sep 17 00:00:00 2001 From: Oleksandr Dzhychko Date: Sun, 26 Oct 2025 12:47:43 +0100 Subject: [PATCH 39/43] fix: pom.xml for org-tweetyproject-arg-explanations --- org-tweetyproject-arg-explanations/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org-tweetyproject-arg-explanations/pom.xml b/org-tweetyproject-arg-explanations/pom.xml index ffcf0ad01..c6512ce01 100644 --- a/org-tweetyproject-arg-explanations/pom.xml +++ b/org-tweetyproject-arg-explanations/pom.xml @@ -13,14 +13,14 @@ org.tweetyproject parent-pom - 1.28-SNAPSHOT + 1.29-SNAPSHOT .. org.tweetyproject.arg dung - 1.28-SNAPSHOT + 1.29-SNAPSHOT compile From ededa203a1c1b452e378486f97a87a76be6eb3eb Mon Sep 17 00:00:00 2001 From: Oleksandr Dzhychko Date: Sun, 26 Oct 2025 12:47:57 +0100 Subject: [PATCH 40/43] fix: invalid Javadoc --- .../tweetyproject/math/examples/TestToQuadraticForm.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/org-tweetyproject-math/src/main/java/org/tweetyproject/math/examples/TestToQuadraticForm.java b/org-tweetyproject-math/src/main/java/org/tweetyproject/math/examples/TestToQuadraticForm.java index 041fe2564..83c698985 100644 --- a/org-tweetyproject-math/src/main/java/org/tweetyproject/math/examples/TestToQuadraticForm.java +++ b/org-tweetyproject-math/src/main/java/org/tweetyproject/math/examples/TestToQuadraticForm.java @@ -33,15 +33,15 @@ */ public class TestToQuadraticForm { - /** - * main method - * @param args arguments - */ // Default constructor public TestToQuadraticForm() { // Default constructor } + /** + * main method + * @param args arguments + */ public static void main(String[] args) { IntegerVariable m1 = new IntegerVariable("Maschine_A"); ArrayList opts = new ArrayList(); From 9d2e34f5b80fb9b98bbedfcec154c0db2fbead9d Mon Sep 17 00:00:00 2001 From: Oleksandr Dzhychko Date: Sun, 26 Oct 2025 17:34:03 +0100 Subject: [PATCH 41/43] fix: disable test for know issue --- .../causal/reasoner/SimpleCausalReasonerTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java b/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java index eef05dd09..266aaaa81 100644 --- a/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java +++ b/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java @@ -18,6 +18,9 @@ */ package org.tweetyproject.causal.reasoner; +import org.junit.jupiter.api.Disabled; + +@Disabled("The implementation of SimpleCausalReasoner has a bug.") public class SimpleCausalReasonerTest extends AbstractCausalReasonerTestBase { @Override From f702b755f62668470c9812017bbae4102f514c00 Mon Sep 17 00:00:00 2001 From: Oleksandr Dzhychko Date: Mon, 27 Oct 2025 10:43:02 +0100 Subject: [PATCH 42/43] fix: set an updated version in pom.xml for org-tweetyproject-causal --- org-tweetyproject-causal/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org-tweetyproject-causal/pom.xml b/org-tweetyproject-causal/pom.xml index 65b7f3e43..b0192f08b 100644 --- a/org-tweetyproject-causal/pom.xml +++ b/org-tweetyproject-causal/pom.xml @@ -9,20 +9,20 @@ org.tweetyproject parent-pom - 1.25-SNAPSHOT + 1.29-SNAPSHOT .. org.tweetyproject.arg dung - 1.25-SNAPSHOT + 1.29-SNAPSHOT compile org.tweetyproject.logics pl - 1.25-SNAPSHOT + 1.29-SNAPSHOT compile From bebf6492c37ab03944a598fa634d253d02874937 Mon Sep 17 00:00:00 2001 From: Oleksandr Dzhychko Date: Mon, 27 Oct 2025 10:49:36 +0100 Subject: [PATCH 43/43] chore: comment out code for SimpleCausalReasoner It has a known bug. We do not make it accidentally usable. But the commented-out code is kept for reference. --- .../CausalReasoningExampleSurfer.java | 2 - .../examples/CausalReasoningExampleVirus.java | 1 - .../causal/reasoner/SimpleCausalReasoner.java | 112 +++++++++--------- .../reasoner/SimpleCausalReasonerTest.java | 23 ++-- 4 files changed, 67 insertions(+), 71 deletions(-) diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java index e9cc7d034..d11a73aab 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleSurfer.java @@ -19,9 +19,7 @@ package org.tweetyproject.causal.examples; import org.tweetyproject.arg.dung.syntax.DungTheory; -import org.tweetyproject.causal.reasoner.AbstractCausalReasoner; import org.tweetyproject.causal.reasoner.ArgumentationBasedCausalReasoner; -import org.tweetyproject.causal.reasoner.SimpleCausalReasoner; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.causal.syntax.StructuralCausalModel; import org.tweetyproject.logics.pl.syntax.*; diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java index 498cdfdca..e7b8fcc06 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/examples/CausalReasoningExampleVirus.java @@ -21,7 +21,6 @@ import org.tweetyproject.arg.dung.syntax.DungTheory; import org.tweetyproject.arg.dung.util.DungTheoryPlotter; import org.tweetyproject.causal.reasoner.ArgumentationBasedCausalReasoner; -import org.tweetyproject.causal.reasoner.SimpleCausalReasoner; import org.tweetyproject.causal.syntax.CausalKnowledgeBase; import org.tweetyproject.causal.syntax.StructuralCausalModel; import org.tweetyproject.logics.pl.syntax.*; diff --git a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/SimpleCausalReasoner.java b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/SimpleCausalReasoner.java index bd00c062f..446d0c01d 100644 --- a/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/SimpleCausalReasoner.java +++ b/org-tweetyproject-causal/src/main/java/org/tweetyproject/causal/reasoner/SimpleCausalReasoner.java @@ -16,59 +16,59 @@ * * Copyright 2024 The TweetyProject Team */ -package org.tweetyproject.causal.reasoner; - -import org.tweetyproject.causal.semantics.CausalInterpretation; -import org.tweetyproject.causal.syntax.CausalKnowledgeBase; -import org.tweetyproject.causal.syntax.StructuralCausalModel; -import org.tweetyproject.commons.InterpretationSet; -import org.tweetyproject.logics.commons.analysis.NaiveMusEnumerator; -import org.tweetyproject.logics.pl.sat.Sat4jSolver; -import org.tweetyproject.logics.pl.sat.SatSolver; -import org.tweetyproject.logics.pl.sat.SimpleModelEnumerator; -import org.tweetyproject.logics.pl.syntax.PlBeliefSet; -import org.tweetyproject.logics.pl.syntax.PlFormula; -import org.tweetyproject.logics.pl.syntax.Proposition; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; - -/** - * Naive reasoner for causal reasoning - * - * @author Lars Bengel - */ -public class SimpleCausalReasoner extends AbstractCausalReasoner { - @Override - public Collection getModels(CausalKnowledgeBase cbase, Collection observations, Map interventions) { - // Perform interventions - StructuralCausalModel model = cbase.getCausalModel(); - for (Proposition atom : interventions.keySet()) { - model = model.intervene(atom, interventions.get(atom)); - } - PlBeliefSet knowledge = new PlBeliefSet(model.getStructuralEquations()); - knowledge.addAll(observations); - - // Enumerate through all maximally consistent subsets of the assumptions and compute the models for each set + knowledge - SatSolver solver = new Sat4jSolver(); - NaiveMusEnumerator enumerator = new NaiveMusEnumerator<>(solver); - - SimpleModelEnumerator modelEnumerator = new SimpleModelEnumerator(); - - Collection> maximalConsistentSubsets = enumerator.maximalConsistentSubsets(cbase.getAssumptions()); - - Collection result = new HashSet<>(); - for (Collection set : maximalConsistentSubsets) { - PlBeliefSet set2 = new PlBeliefSet(knowledge); - set2.addAll(set); - for (InterpretationSet itp : modelEnumerator.getModels(set2)) { - CausalInterpretation interpretation = new CausalInterpretation(); - interpretation.addAll(itp); - result.add(interpretation); - } - } - - return result; - } -} +//package org.tweetyproject.causal.reasoner; +// +//import org.tweetyproject.causal.semantics.CausalInterpretation; +//import org.tweetyproject.causal.syntax.CausalKnowledgeBase; +//import org.tweetyproject.causal.syntax.StructuralCausalModel; +//import org.tweetyproject.commons.InterpretationSet; +//import org.tweetyproject.logics.commons.analysis.NaiveMusEnumerator; +//import org.tweetyproject.logics.pl.sat.Sat4jSolver; +//import org.tweetyproject.logics.pl.sat.SatSolver; +//import org.tweetyproject.logics.pl.sat.SimpleModelEnumerator; +//import org.tweetyproject.logics.pl.syntax.PlBeliefSet; +//import org.tweetyproject.logics.pl.syntax.PlFormula; +//import org.tweetyproject.logics.pl.syntax.Proposition; +// +//import java.util.Collection; +//import java.util.HashSet; +//import java.util.Map; +// +///** +// * Naive reasoner for causal reasoning +// * +// * @author Lars Bengel +// */ +//public class SimpleCausalReasoner extends AbstractCausalReasoner { +// @Override +// public Collection getModels(CausalKnowledgeBase cbase, Collection observations, Map interventions) { +// // Perform interventions +// StructuralCausalModel model = cbase.getCausalModel(); +// for (Proposition atom : interventions.keySet()) { +// model = model.intervene(atom, interventions.get(atom)); +// } +// PlBeliefSet knowledge = new PlBeliefSet(model.getStructuralEquations()); +// knowledge.addAll(observations); +// +// // Enumerate through all maximally consistent subsets of the assumptions and compute the models for each set + knowledge +// SatSolver solver = new Sat4jSolver(); +// NaiveMusEnumerator enumerator = new NaiveMusEnumerator<>(solver); +// +// SimpleModelEnumerator modelEnumerator = new SimpleModelEnumerator(); +// +// Collection> maximalConsistentSubsets = enumerator.maximalConsistentSubsets(cbase.getAssumptions()); +// +// Collection result = new HashSet<>(); +// for (Collection set : maximalConsistentSubsets) { +// PlBeliefSet set2 = new PlBeliefSet(knowledge); +// set2.addAll(set); +// for (InterpretationSet itp : modelEnumerator.getModels(set2)) { +// CausalInterpretation interpretation = new CausalInterpretation(); +// interpretation.addAll(itp); +// result.add(interpretation); +// } +// } +// +// return result; +// } +//} diff --git a/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java b/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java index 266aaaa81..1bb57c418 100644 --- a/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java +++ b/org-tweetyproject-causal/src/test/java/org/tweetyproject/causal/reasoner/SimpleCausalReasonerTest.java @@ -16,15 +16,14 @@ * * Copyright 2025 The TweetyProject Team */ -package org.tweetyproject.causal.reasoner; - -import org.junit.jupiter.api.Disabled; - -@Disabled("The implementation of SimpleCausalReasoner has a bug.") -public class SimpleCausalReasonerTest extends AbstractCausalReasonerTestBase { - - @Override - protected SimpleCausalReasoner createReasoner() { - return new SimpleCausalReasoner(); - } -} \ No newline at end of file +//package org.tweetyproject.causal.reasoner; +// +//import org.junit.jupiter.api.Disabled; +// +//public class SimpleCausalReasonerTest extends AbstractCausalReasonerTestBase { +// +// @Override +// protected SimpleCausalReasoner createReasoner() { +// return new SimpleCausalReasoner(); +// } +//} \ No newline at end of file