|
| 1 | +/* |
| 2 | + * This file is part of "TweetyProject", a collection of Java libraries for |
| 3 | + * logical aspects of artificial intelligence and knowledge representation. |
| 4 | + * |
| 5 | + * TweetyProject is free software: you can redistribute it and/or modify |
| 6 | + * it under the terms of the GNU Lesser General Public License version 3 as |
| 7 | + * published by the Free Software Foundation. |
| 8 | + * |
| 9 | + * This program is distributed in the hope that it will be useful, |
| 10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | + * GNU Lesser General Public License for more details. |
| 13 | + * |
| 14 | + * You should have received a copy of the GNU Lesser General Public License |
| 15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | + * |
| 17 | + * Copyright 2025 The TweetyProject Team <http://tweetyproject.org/contact/> |
| 18 | + */ |
| 19 | +package org.tweetyproject.arg.dung.equivalence; |
| 20 | + |
| 21 | +import org.tweetyproject.arg.dung.equivalence.kernel.EquivalenceKernel; |
| 22 | +import org.tweetyproject.arg.dung.reasoner.AbstractExtensionReasoner; |
| 23 | +import org.tweetyproject.arg.dung.semantics.Semantics; |
| 24 | +import org.tweetyproject.arg.dung.syntax.Argument; |
| 25 | +import org.tweetyproject.arg.dung.syntax.DungTheory; |
| 26 | +import org.tweetyproject.commons.util.SetTools; |
| 27 | + |
| 28 | +import java.util.Collection; |
| 29 | +import java.util.HashSet; |
| 30 | +import java.util.Set; |
| 31 | + |
| 32 | + |
| 33 | +/** |
| 34 | + * This class defines normal deletion equivalence for {@link DungTheory argumentation frameworks} wrt. some {@link Semantics semantics}, |
| 35 | + * i.e., two AFs F and G are normal deletion equivalent iff they possess the same set of |
| 36 | + * {@link org.tweetyproject.arg.dung.semantics.Extension extensions} wrt. the {@link Semantics semantics} under every normal deletion. |
| 37 | + * A normal deletion deletes a set of arguments together with all their corresponding attacks. |
| 38 | + * |
| 39 | + * @see "Ringo Baumann. 'Context-free and context-sensitive kernels: Update and deletion equivalence in abstract argumentation' ECAI14 (2014) pp. 63–68" |
| 40 | + * |
| 41 | + * @author Lars Bengel |
| 42 | + */ |
| 43 | +public class NormalDeletionEquivalence implements Equivalence<DungTheory> { |
| 44 | + /** the semantics of this equivalence instance **/ |
| 45 | + private final Semantics semantics; |
| 46 | + |
| 47 | + /** |
| 48 | + * Initializes a new instance of this equivalence wrt. the given semantics |
| 49 | + * @param semantics some semantics |
| 50 | + */ |
| 51 | + public NormalDeletionEquivalence(Semantics semantics) { |
| 52 | + this.semantics = semantics; |
| 53 | + } |
| 54 | + |
| 55 | + @Override |
| 56 | + public boolean isEquivalent(DungTheory obj1, DungTheory obj2) { |
| 57 | + switch (semantics) { |
| 58 | + case CO,GR,SAD -> { |
| 59 | + if (!loop(obj1, obj2)) return false; |
| 60 | + if (!coAtt(obj1, obj2)) return false; |
| 61 | + Collection<Argument> shared = new SetTools<Argument>().getIntersection(new HashSet<>(obj1), new HashSet<>(obj2)); |
| 62 | + EquivalenceKernel kernel = EquivalenceKernel.getStrongExpansionEquivalenceKernelForSemantics(semantics); |
| 63 | + return kernel.getKernel((DungTheory) obj1.getRestriction(shared)).equals(kernel.getKernel((DungTheory) obj2.getRestriction(shared))); |
| 64 | + } case ADM,PR,UC,ID -> { |
| 65 | + if (!loop(obj1, obj2)) return false; |
| 66 | + if (!admAtt(obj1, obj2)) return false; |
| 67 | + Collection<Argument> shared = new SetTools<Argument>().getIntersection(new HashSet<>(obj1), new HashSet<>(obj2)); |
| 68 | + EquivalenceKernel kernel = EquivalenceKernel.getStrongExpansionEquivalenceKernelForSemantics(semantics); |
| 69 | + return kernel.getKernel((DungTheory) obj1.getRestriction(shared)).equals(kernel.getKernel((DungTheory) obj2.getRestriction(shared))); |
| 70 | + } case ST -> { |
| 71 | + return new StrongEquivalence(Semantics.ST).isEquivalent(obj1, obj2); |
| 72 | + } case SST,EA -> { |
| 73 | + return isNormalDeletionEquivalent(obj1,obj2,semantics); |
| 74 | + } default -> throw new IllegalArgumentException("unsupported semantics"); |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + @Override |
| 79 | + public boolean isEquivalent(Collection<DungTheory> objects) { |
| 80 | + throw new UnsupportedOperationException("not implemented"); |
| 81 | + } |
| 82 | + |
| 83 | + @Override |
| 84 | + public String getName() { |
| 85 | + return "Normal Deletion Equivalence"; |
| 86 | + } |
| 87 | + |
| 88 | + /** |
| 89 | + * Naively checks whether two AFs are normal deletion equivalence by comparing the extensions of every normal deletion |
| 90 | + * |
| 91 | + * @param theory1 some argumentation framework |
| 92 | + * @param theory2 some argumentation framework |
| 93 | + * @param semantics some semantics |
| 94 | + * @return 'true' iff both AFs are normal deletion equivalent under the given semantics |
| 95 | + */ |
| 96 | + public static boolean isNormalDeletionEquivalent(DungTheory theory1, DungTheory theory2, Semantics semantics) { |
| 97 | + AbstractExtensionReasoner reasoner = AbstractExtensionReasoner.getSimpleReasonerForSemantics(semantics); |
| 98 | + Set<Argument> arguments = new HashSet<>(theory1); |
| 99 | + arguments.addAll(theory2); |
| 100 | + for (Set<Argument> subset: new SetTools<Argument>().subsets(arguments)) { |
| 101 | + DungTheory th1 = theory1.clone(); |
| 102 | + th1.removeAll(subset); |
| 103 | + DungTheory th2 = theory2.clone(); |
| 104 | + th2.removeAll(subset); |
| 105 | + if (!reasoner.getModels(th1).equals(reasoner.getModels(th2))) { |
| 106 | + //System.out.println(subset); |
| 107 | + //System.out.println(th1); |
| 108 | + //System.out.println(th2); |
| 109 | + return false; |
| 110 | + } |
| 111 | + } |
| 112 | + return true; |
| 113 | + } |
| 114 | + |
| 115 | + private boolean loop(DungTheory theory1, DungTheory theory2) { |
| 116 | + Collection<Argument> symDif = new SetTools<Argument>().symmetricDifference(theory1, theory2); |
| 117 | + DungTheory combi = theory1.clone(); |
| 118 | + combi.add(theory2); |
| 119 | + return symDif.equals(getSelfLoops((DungTheory) combi.getRestriction(symDif))); |
| 120 | + } |
| 121 | + |
| 122 | + private boolean coAtt(DungTheory theory1, DungTheory theory2) { |
| 123 | + Collection<Argument> args1 = new HashSet<>(theory1); |
| 124 | + args1.removeAll(theory2); |
| 125 | + Collection<Argument> args2 = new HashSet<>(theory2); |
| 126 | + args2.removeAll(theory1); |
| 127 | + |
| 128 | + for (Argument b: args1) { |
| 129 | + for (Argument a: getNonSelfLoops((DungTheory) theory1.getRestriction(new SetTools<Argument>().getIntersection(new HashSet<>(theory1), new HashSet<>(theory2))))) { |
| 130 | + if (theory1.isAttackedBy(a,b)) { |
| 131 | + return false; |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | + for (Argument b: args2) { |
| 136 | + for (Argument a: getNonSelfLoops((DungTheory) theory2.getRestriction(new SetTools<Argument>().getIntersection(new HashSet<>(theory1), new HashSet<>(theory2))))) { |
| 137 | + if (theory2.isAttackedBy(a,b)) { |
| 138 | + return false; |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + return true; |
| 143 | + } |
| 144 | + |
| 145 | + private boolean admAtt(DungTheory theory1, DungTheory theory2) { |
| 146 | + Collection<Argument> args1 = new HashSet<>(theory1); |
| 147 | + args1.removeAll(theory2); |
| 148 | + Collection<Argument> args2 = new HashSet<>(theory2); |
| 149 | + args2.removeAll(theory1); |
| 150 | + |
| 151 | + for (Argument b: args1) { |
| 152 | + for (Argument a: getNonSelfLoops((DungTheory) theory1.getRestriction(new SetTools<Argument>().getIntersection(new HashSet<>(theory1), new HashSet<>(theory2))))) { |
| 153 | + if (theory1.isAttackedBy(a,b) && !theory1.isAttackedBy(b,a)) { |
| 154 | + return false; |
| 155 | + } |
| 156 | + } |
| 157 | + } |
| 158 | + for (Argument b: args2) { |
| 159 | + for (Argument a: getNonSelfLoops((DungTheory) theory2.getRestriction(new SetTools<Argument>().getIntersection(new HashSet<>(theory1), new HashSet<>(theory2))))) { |
| 160 | + if (theory2.isAttackedBy(a,b) && !theory2.isAttackedBy(b,a)) { |
| 161 | + return false; |
| 162 | + } |
| 163 | + } |
| 164 | + } |
| 165 | + return true; |
| 166 | + } |
| 167 | + |
| 168 | + private Collection<Argument> getSelfLoops(DungTheory theory) { |
| 169 | + Collection<Argument> result = new HashSet<>(); |
| 170 | + for (Argument arg : theory) { |
| 171 | + if (theory.isAttackedBy(arg,arg)) { |
| 172 | + result.add(arg); |
| 173 | + } |
| 174 | + } |
| 175 | + return result; |
| 176 | + } |
| 177 | + |
| 178 | + private Collection<Argument> getNonSelfLoops(DungTheory theory) { |
| 179 | + Collection<Argument> result = new HashSet<>(); |
| 180 | + for (Argument arg : theory) { |
| 181 | + if (!theory.isAttackedBy(arg,arg)) { |
| 182 | + result.add(arg); |
| 183 | + } |
| 184 | + } |
| 185 | + return result; |
| 186 | + } |
| 187 | +} |
0 commit comments