youssef commited on
Commit
ab9e430
·
1 Parent(s): 8bdd43d

added deepcopies to fix normal attack generation

Browse files
Files changed (4) hide show
  1. aba/aba_builder.py +21 -20
  2. aba/aba_framework.py +28 -12
  3. aba/parsing.py +11 -6
  4. aba/test.py +78 -0
aba/aba_builder.py CHANGED
@@ -11,10 +11,10 @@ import os
11
  def build_aba_framework(doc_path: str) -> ABAFramework:
12
  """
13
  Build an ABAFramework object from a structured document.
14
-
15
  Args:
16
  doc_path (str): Path to the document to parse.
17
-
18
  Returns:
19
  ABAFramework: An ABAFramework instance containing:
20
  - language (Set[Literal]): Set of Literal objects.
@@ -22,7 +22,7 @@ def build_aba_framework(doc_path: str) -> ABAFramework:
22
  - assumptions (Set[Literal]): Set of assumptions.
23
  - contraries (Set[Contrary]): Set of contraries.
24
  - preferences (Dict[Literal, Set[Literal]]): Preference mappings.
25
-
26
  Example:
27
  >>> aba = build_aba_framework("./backend/doc.txt")
28
  >>> isinstance(next(iter(aba.language)), Literal)
@@ -33,20 +33,20 @@ def build_aba_framework(doc_path: str) -> ABAFramework:
33
  # Parse the document
34
  language_parse, assumptions_parse, contraries_parse, rules_parse, preferences_parse = parse_doc(
35
  doc_path)
36
-
37
  # Initialize containers
38
  language: Dict[str, Literal] = {}
39
  rules: Set[Rule] = set()
40
  contraries: Set[Contrary] = set()
41
  assumptions: Set[Literal] = set()
42
  preferences: Dict[Literal, Set[Literal]] = {}
43
-
44
  # Language: build Literal objects
45
  for lit in language_parse:
46
  language[lit] = Literal(lit)
47
-
48
  language_set: Set[Literal] = set(language.values())
49
-
50
  # Rules: convert parsed structure into Rule objects
51
  for rule in rules_parse:
52
  r_id = next(iter(rule))
@@ -54,29 +54,30 @@ def build_aba_framework(doc_path: str) -> ABAFramework:
54
  body_atoms = rule[r_id][head]
55
  body_literals = {language[i] for i in body_atoms if i in language}
56
  rules.add(Rule(r_id, language[head], body_literals))
57
-
58
  # Contraries: build Contrary objects
59
  for lit1, lit2 in contraries_parse:
60
  contraries.add(Contrary(language[lit1], language[lit2]))
61
-
62
  # Assumptions: convert to set of Literal
63
  for lit in assumptions_parse:
64
  assumptions.add(language[lit])
65
-
66
  # Preferences: merge all preference dictionaries and convert to Literal objects
67
  for pref_dict in preferences_parse:
68
  for lit_str, less_preferred_strs in pref_dict.items():
69
  if lit_str in language:
70
  lit_obj = language[lit_str]
71
  # Convert string literals to Literal objects
72
- less_preferred_objs = {language[lp] for lp in less_preferred_strs if lp in language}
73
-
 
74
  # Merge with existing preferences for this literal
75
  if lit_obj in preferences:
76
  preferences[lit_obj].update(less_preferred_objs)
77
  else:
78
  preferences[lit_obj] = less_preferred_objs
79
-
80
  # Build ABA framework
81
  aba_framework = ABAFramework(
82
  language=language_set,
@@ -85,7 +86,7 @@ def build_aba_framework(doc_path: str) -> ABAFramework:
85
  contraries=contraries,
86
  preferences=preferences
87
  )
88
-
89
  return aba_framework
90
 
91
 
@@ -93,26 +94,26 @@ def prepare_aba_plus_framework(aba_framework: ABAFramework) -> ABAFramework:
93
  """
94
  Prepare an ABA framework for ABA+ by ensuring it's atomic and generating
95
  all necessary components.
96
-
97
  Args:
98
  aba_framework: The ABA framework to prepare
99
-
100
  Returns:
101
  ABAFramework: The prepared framework (modified in place)
102
  """
103
-
104
  # Generate arguments for atomic framework
105
  print("\nGenerating arguments for atomic framework...")
106
  aba_framework.arguments.clear()
107
  aba_framework.generate_arguments()
108
  print(f"Generated {len(aba_framework.arguments)} arguments")
109
-
110
  # Generate standard attacks
111
  print("\nGenerating standard attacks for atomic framework...")
112
  aba_framework.attacks.clear()
113
  aba_framework.generate_attacks()
114
  print(f"Generated {len(aba_framework.attacks)} attacks\n")
115
-
116
  return aba_framework
117
 
118
 
@@ -140,4 +141,4 @@ def build_aba_framework_from_text(text: str) -> ABAFramework:
140
  except OSError:
141
  pass
142
 
143
- return aba
 
11
  def build_aba_framework(doc_path: str) -> ABAFramework:
12
  """
13
  Build an ABAFramework object from a structured document.
14
+
15
  Args:
16
  doc_path (str): Path to the document to parse.
17
+
18
  Returns:
19
  ABAFramework: An ABAFramework instance containing:
20
  - language (Set[Literal]): Set of Literal objects.
 
22
  - assumptions (Set[Literal]): Set of assumptions.
23
  - contraries (Set[Contrary]): Set of contraries.
24
  - preferences (Dict[Literal, Set[Literal]]): Preference mappings.
25
+
26
  Example:
27
  >>> aba = build_aba_framework("./backend/doc.txt")
28
  >>> isinstance(next(iter(aba.language)), Literal)
 
33
  # Parse the document
34
  language_parse, assumptions_parse, contraries_parse, rules_parse, preferences_parse = parse_doc(
35
  doc_path)
36
+
37
  # Initialize containers
38
  language: Dict[str, Literal] = {}
39
  rules: Set[Rule] = set()
40
  contraries: Set[Contrary] = set()
41
  assumptions: Set[Literal] = set()
42
  preferences: Dict[Literal, Set[Literal]] = {}
43
+
44
  # Language: build Literal objects
45
  for lit in language_parse:
46
  language[lit] = Literal(lit)
47
+
48
  language_set: Set[Literal] = set(language.values())
49
+
50
  # Rules: convert parsed structure into Rule objects
51
  for rule in rules_parse:
52
  r_id = next(iter(rule))
 
54
  body_atoms = rule[r_id][head]
55
  body_literals = {language[i] for i in body_atoms if i in language}
56
  rules.add(Rule(r_id, language[head], body_literals))
57
+
58
  # Contraries: build Contrary objects
59
  for lit1, lit2 in contraries_parse:
60
  contraries.add(Contrary(language[lit1], language[lit2]))
61
+
62
  # Assumptions: convert to set of Literal
63
  for lit in assumptions_parse:
64
  assumptions.add(language[lit])
65
+
66
  # Preferences: merge all preference dictionaries and convert to Literal objects
67
  for pref_dict in preferences_parse:
68
  for lit_str, less_preferred_strs in pref_dict.items():
69
  if lit_str in language:
70
  lit_obj = language[lit_str]
71
  # Convert string literals to Literal objects
72
+ less_preferred_objs = {
73
+ language[lp] for lp in less_preferred_strs if lp in language}
74
+
75
  # Merge with existing preferences for this literal
76
  if lit_obj in preferences:
77
  preferences[lit_obj].update(less_preferred_objs)
78
  else:
79
  preferences[lit_obj] = less_preferred_objs
80
+
81
  # Build ABA framework
82
  aba_framework = ABAFramework(
83
  language=language_set,
 
86
  contraries=contraries,
87
  preferences=preferences
88
  )
89
+
90
  return aba_framework
91
 
92
 
 
94
  """
95
  Prepare an ABA framework for ABA+ by ensuring it's atomic and generating
96
  all necessary components.
97
+
98
  Args:
99
  aba_framework: The ABA framework to prepare
100
+
101
  Returns:
102
  ABAFramework: The prepared framework (modified in place)
103
  """
104
+
105
  # Generate arguments for atomic framework
106
  print("\nGenerating arguments for atomic framework...")
107
  aba_framework.arguments.clear()
108
  aba_framework.generate_arguments()
109
  print(f"Generated {len(aba_framework.arguments)} arguments")
110
+
111
  # Generate standard attacks
112
  print("\nGenerating standard attacks for atomic framework...")
113
  aba_framework.attacks.clear()
114
  aba_framework.generate_attacks()
115
  print(f"Generated {len(aba_framework.attacks)} attacks\n")
116
+
117
  return aba_framework
118
 
119
 
 
141
  except OSError:
142
  pass
143
 
144
+ return aba
aba/aba_framework.py CHANGED
@@ -10,6 +10,7 @@ Features:
10
  - Provides visualization of attack graphs using PyVis.
11
  """
12
 
 
13
  from .argument import Argument
14
  from .contrary import Contrary
15
  from .literal import Literal
@@ -189,7 +190,7 @@ class ABAFramework:
189
 
190
  # ------------------------- ABA Methods -------------------------
191
 
192
- def transform_aba(self) -> None:
193
  """
194
  Transforms the ABA framework to ensure it is both non-circular and atomic.
195
 
@@ -204,12 +205,15 @@ class ABAFramework:
204
  print("\n ------- Transforming ABA framework -------\n")
205
  if self.is_aba_circular():
206
  print("The ABA Framework is circular\n")
207
- self.make_aba_not_circular()
208
  elif not self.is_aba_atomic():
209
  print("The ABA Framework is not atomic\n")
210
- self.make_aba_atomic()
 
 
 
211
 
212
- def make_aba_atomic(self) -> None:
213
  """
214
  Transforms the ABA framework into an atomic one.
215
 
@@ -236,6 +240,8 @@ class ABAFramework:
236
  R = { a <- xd }
237
  Contraries = { xd̄ = xnd, xnd̄ = x }
238
  """
 
 
239
  new_language = set(self.language)
240
  new_assumptions = set(self.assumptions)
241
  new_rules = set()
@@ -265,10 +271,16 @@ class ABAFramework:
265
  new_rules.add(Rule(rule.rule_name, rule.head, new_body))
266
 
267
  # Step 3: Update framework
268
- self.language = new_language
269
- self.assumptions = new_assumptions
270
- self.rules = new_rules
271
- self.contraries = new_contraries
 
 
 
 
 
 
272
 
273
  def is_aba_atomic(self) -> bool:
274
  """
@@ -335,7 +347,7 @@ class ABAFramework:
335
  return True # Cycle found
336
  return False # No cycles
337
 
338
- def make_aba_not_circular(self) -> None:
339
  """
340
  Transforms the ABA framework to a non-circular one by renaming heads and bodies of rules.
341
 
@@ -368,7 +380,7 @@ class ABAFramework:
368
  x1 <- a
369
  x <- a
370
  """
371
-
372
  k = len(self.language) - len(self.assumptions)
373
  new_language = set(self.language)
374
  new_rules = set()
@@ -395,8 +407,12 @@ class ABAFramework:
395
  new_rule_name = f"{rule.rule_name}_{i+1}"
396
  new_rules.add(Rule(new_rule_name, new_head, new_body))
397
 
398
- self.language = new_language
399
- self.rules = new_rules
 
 
 
 
400
 
401
  # ------------------------- ABA+ Methods -------------------------
402
 
 
10
  - Provides visualization of attack graphs using PyVis.
11
  """
12
 
13
+ from copy import deepcopy
14
  from .argument import Argument
15
  from .contrary import Contrary
16
  from .literal import Literal
 
190
 
191
  # ------------------------- ABA Methods -------------------------
192
 
193
+ def transform_aba(self) -> "ABAFramework":
194
  """
195
  Transforms the ABA framework to ensure it is both non-circular and atomic.
196
 
 
205
  print("\n ------- Transforming ABA framework -------\n")
206
  if self.is_aba_circular():
207
  print("The ABA Framework is circular\n")
208
+ return self.make_aba_not_circular()
209
  elif not self.is_aba_atomic():
210
  print("The ABA Framework is not atomic\n")
211
+ return self.make_aba_atomic()
212
+ else:
213
+ print("The ABA Framework is already non-circular and atomic\n")
214
+ return deepcopy(self)
215
 
216
+ def make_aba_atomic(self) -> "ABAFramework":
217
  """
218
  Transforms the ABA framework into an atomic one.
219
 
 
240
  R = { a <- xd }
241
  Contraries = { xd̄ = xnd, xnd̄ = x }
242
  """
243
+ new_framework = deepcopy(self)
244
+
245
  new_language = set(self.language)
246
  new_assumptions = set(self.assumptions)
247
  new_rules = set()
 
271
  new_rules.add(Rule(rule.rule_name, rule.head, new_body))
272
 
273
  # Step 3: Update framework
274
+ # self.language = new_language
275
+ # self.assumptions = new_assumptions
276
+ # self.rules = new_rules
277
+ # self.contraries = new_contraries
278
+ new_framework.language = new_language
279
+ new_framework.assumptions = new_assumptions
280
+ new_framework.rules = new_rules
281
+ new_framework.contraries = new_contraries
282
+
283
+ return new_framework
284
 
285
  def is_aba_atomic(self) -> bool:
286
  """
 
347
  return True # Cycle found
348
  return False # No cycles
349
 
350
+ def make_aba_not_circular(self) -> "ABAFramework":
351
  """
352
  Transforms the ABA framework to a non-circular one by renaming heads and bodies of rules.
353
 
 
380
  x1 <- a
381
  x <- a
382
  """
383
+ new_copy = deepcopy(self)
384
  k = len(self.language) - len(self.assumptions)
385
  new_language = set(self.language)
386
  new_rules = set()
 
407
  new_rule_name = f"{rule.rule_name}_{i+1}"
408
  new_rules.add(Rule(new_rule_name, new_head, new_body))
409
 
410
+ # self.language = new_language
411
+ # self.rules = new_rules
412
+ new_copy.language = new_language
413
+ new_copy.rules = new_rules
414
+
415
+ return new_copy
416
 
417
  # ------------------------- ABA+ Methods -------------------------
418
 
aba/parsing.py CHANGED
@@ -1,6 +1,7 @@
1
  import re
2
  from typing import List, Tuple, Dict, Set
3
 
 
4
  def _parse_list(content: str) -> List[str]:
5
  """
6
  Parse a string representing a list of literals into a Python list of strings.
@@ -23,6 +24,7 @@ def _parse_list(content: str) -> List[str]:
23
  return []
24
  return [x.strip() for x in content.split(",") if x.strip()]
25
 
 
26
  def _parse_rule_line(line: str) -> Dict[str, Dict[str, Set[str]]]:
27
  """
28
  Parse a single rule line of the form [rX]: head <- body.
@@ -45,14 +47,15 @@ def _parse_rule_line(line: str) -> Dict[str, Dict[str, Set[str]]]:
45
  return {r_id: {head: body_atoms}}
46
  return {}
47
 
 
48
  def _parse_pref_line(line: str) -> Dict[str, Set[str]]:
49
  """
50
  Parse a preference line of the form:
51
  - PREF: a,b > c,d > e
52
  - PREF: a,b > c and a > d
53
-
54
  Supports multiple chains joined by "and".
55
-
56
  Returns:
57
  Dict[str, Set[str]]: Dictionary mapping each element to all elements
58
  that come after it in the preference chain(s).
@@ -83,6 +86,7 @@ def _parse_pref_line(line: str) -> Dict[str, Set[str]]:
83
 
84
  return pref_dict
85
 
 
86
  def parse_doc(path: str) -> Tuple[
87
  List[str],
88
  List[str],
@@ -120,13 +124,13 @@ def parse_doc(path: str) -> Tuple[
120
  contraries: List[Tuple[str, str]] = []
121
  rules: List[Dict[str, Dict[str, Set[str]]]] = []
122
  preferences: List[Dict[str, Set[str]]] = []
123
-
124
  with open(path, "r") as f:
125
  for line in f:
126
  line = line.strip()
127
  if not line:
128
  continue
129
-
130
  if line.startswith("L:"):
131
  language = _parse_list(line.split(":", 1)[1])
132
  elif line.startswith("A:"):
@@ -143,12 +147,13 @@ def parse_doc(path: str) -> Tuple[
143
  pref_dict = _parse_pref_line(line)
144
  if pref_dict:
145
  preferences.append(pref_dict)
146
-
147
  return language, assumptions, contraries, rules, preferences
148
 
149
 
150
  if __name__ == "__main__":
151
- language, assumptions, contraries, rules, preferences = parse_doc("./backend/data/simple_plus2AND.txt")
 
152
  print("Language:", language)
153
  print("Assumptions:", assumptions)
154
  print("Contraries:", contraries)
 
1
  import re
2
  from typing import List, Tuple, Dict, Set
3
 
4
+
5
  def _parse_list(content: str) -> List[str]:
6
  """
7
  Parse a string representing a list of literals into a Python list of strings.
 
24
  return []
25
  return [x.strip() for x in content.split(",") if x.strip()]
26
 
27
+
28
  def _parse_rule_line(line: str) -> Dict[str, Dict[str, Set[str]]]:
29
  """
30
  Parse a single rule line of the form [rX]: head <- body.
 
47
  return {r_id: {head: body_atoms}}
48
  return {}
49
 
50
+
51
  def _parse_pref_line(line: str) -> Dict[str, Set[str]]:
52
  """
53
  Parse a preference line of the form:
54
  - PREF: a,b > c,d > e
55
  - PREF: a,b > c and a > d
56
+
57
  Supports multiple chains joined by "and".
58
+
59
  Returns:
60
  Dict[str, Set[str]]: Dictionary mapping each element to all elements
61
  that come after it in the preference chain(s).
 
86
 
87
  return pref_dict
88
 
89
+
90
  def parse_doc(path: str) -> Tuple[
91
  List[str],
92
  List[str],
 
124
  contraries: List[Tuple[str, str]] = []
125
  rules: List[Dict[str, Dict[str, Set[str]]]] = []
126
  preferences: List[Dict[str, Set[str]]] = []
127
+
128
  with open(path, "r") as f:
129
  for line in f:
130
  line = line.strip()
131
  if not line:
132
  continue
133
+
134
  if line.startswith("L:"):
135
  language = _parse_list(line.split(":", 1)[1])
136
  elif line.startswith("A:"):
 
147
  pref_dict = _parse_pref_line(line)
148
  if pref_dict:
149
  preferences.append(pref_dict)
150
+
151
  return language, assumptions, contraries, rules, preferences
152
 
153
 
154
  if __name__ == "__main__":
155
+ language, assumptions, contraries, rules, preferences = parse_doc(
156
+ "./backend/data/simple_plus2AND.txt")
157
  print("Language:", language)
158
  print("Assumptions:", assumptions)
159
  print("Contraries:", contraries)
aba/test.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Main entry point for generating and analyzing an ABA+ framework.
3
+
4
+ This script:
5
+ 1. Builds an ABA framework from a text specification.
6
+ 2. Prints the original (classical) ABA framework.
7
+ 3. Prepares the framework for ABA+ (atomic transformation + argument/attack generation).
8
+ 4. Generates ABA+ components (assumption combinations, normal/reverse attacks).
9
+ 5. Prints the resulting ABA+ framework components.
10
+ 6. Plots the ABA+ attack graph between sets of assumptions.
11
+ """
12
+
13
+ from copy import deepcopy
14
+ from aba.aba_builder import build_aba_framework, prepare_aba_plus_framework
15
+ from aba.aba_utils import print_aba_plus_results
16
+ from aba.aba_framework import ABAFramework
17
+
18
+
19
+ def testABA(aba_framework: ABAFramework):
20
+
21
+ copy_framework = deepcopy(aba_framework)
22
+
23
+ transformed_framework: ABAFramework = copy_framework.transform_aba()
24
+ print("\n ------- Transformed ABA framework -------\n ")
25
+ print(transformed_framework)
26
+
27
+ # Generate arguments
28
+ transformed_framework.generate_arguments()
29
+ gen_args = transformed_framework.arguments
30
+ print("\n ------- Generated arguments -------\n ")
31
+ print(gen_args)
32
+
33
+ # Generate attacks
34
+ transformed_framework.generate_attacks()
35
+ attacks = transformed_framework.attacks
36
+ print("\n ------- Generated attacks -------\n ")
37
+ print(attacks, "\n")
38
+
39
+
40
+ def testABAPlus(aba_framework: ABAFramework):
41
+
42
+ # === Step 2: Prepare the framework for ABA+ ===
43
+
44
+ aba_framework: ABAFramework = prepare_aba_plus_framework(aba_framework)
45
+
46
+ # === Step 3: Generate ABA+ components ===
47
+ print("\n" + "=" * 50)
48
+ print("Generating ABA+ Components")
49
+ print("=" * 50)
50
+ aba_framework.make_aba_plus()
51
+
52
+ # === Step 4: Print ABA+ results ===
53
+ print_aba_plus_results(aba_framework)
54
+ return aba_framework
55
+
56
+
57
+ def main():
58
+ """
59
+ Main function to generate and analyze an ABA+ framework.
60
+ """
61
+ # === Step 1: Build the ABA framework from input file ===
62
+ print("\n" + "=" * 50)
63
+ print("Building ABA+ Framework")
64
+ print("=" * 50)
65
+
66
+ # Build framework from the given input specification file
67
+ aba_framework = build_aba_framework("aba\exemples\exemple.txt")
68
+ print(f"\n ------- Original ABA framework -------\n{aba_framework}")
69
+
70
+ base_framework = deepcopy(aba_framework)
71
+ testABA(base_framework)
72
+
73
+ aba_for_plus = deepcopy(aba_framework)
74
+ testABAPlus(aba_for_plus)
75
+
76
+
77
+ if __name__ == "__main__":
78
+ main()