arg2p 0.19.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. arg2p/__init__.py +63 -0
  2. arg2p/__main__.py +49 -0
  3. arg2p/_jvm.py +145 -0
  4. arg2p/_version.py +2 -0
  5. arg2p/arg2p.py +271 -0
  6. arg2p/flags.py +224 -0
  7. arg2p/jars/Java-WebSocket-1.6.0.jar +0 -0
  8. arg2p/jars/annotations-13.0.jar +0 -0
  9. arg2p/jars/arg2p-jvm-0.19.0.jar +0 -0
  10. arg2p/jars/causality-solver-jvm-0.19.0.jar +0 -0
  11. arg2p/jars/core-jvm-0.19.0.jar +0 -0
  12. arg2p/jars/core-jvm-3.0.5.jar +0 -0
  13. arg2p/jars/dsl-core-jvm-3.0.5.jar +0 -0
  14. arg2p/jars/dsl-solve-jvm-3.0.5.jar +0 -0
  15. arg2p/jars/dsl-theory-jvm-3.0.5.jar +0 -0
  16. arg2p/jars/dsl-unify-jvm-3.0.5.jar +0 -0
  17. arg2p/jars/io-lib-jvm-3.0.5.jar +0 -0
  18. arg2p/jars/kotlin-reflect-2.4.21.jar +0 -0
  19. arg2p/jars/kotlin-stdlib-2.4.21.jar +0 -0
  20. arg2p/jars/kotlin-stdlib-jdk7-2.4.21.jar +0 -0
  21. arg2p/jars/kotlin-stdlib-jdk8-2.4.21.jar +0 -0
  22. arg2p/jars/kt-math-jvm-0.10.2.jar +0 -0
  23. arg2p/jars/okio-jvm-3.18.2.jar +0 -0
  24. arg2p/jars/parallel-solver-jvm-0.19.0.jar +0 -0
  25. arg2p/jars/parser-core-jvm-3.0.5.jar +0 -0
  26. arg2p/jars/parser-impl-jvm-3.0.5.jar +0 -0
  27. arg2p/jars/parser-theory-jvm-3.0.5.jar +0 -0
  28. arg2p/jars/slf4j-api-2.0.13.jar +0 -0
  29. arg2p/jars/solve-jvm-3.0.5.jar +0 -0
  30. arg2p/jars/theory-jvm-3.0.5.jar +0 -0
  31. arg2p/jars/unify-jvm-3.0.5.jar +0 -0
  32. arg2p/jars/utils-jvm-3.0.5.jar +0 -0
  33. arg2p/model.py +102 -0
  34. arg2p/parallel.py +160 -0
  35. arg2p/py.typed +0 -0
  36. arg2p/values.py +59 -0
  37. arg2p-0.19.0.dist-info/METADATA +61 -0
  38. arg2p-0.19.0.dist-info/RECORD +41 -0
  39. arg2p-0.19.0.dist-info/WHEEL +5 -0
  40. arg2p-0.19.0.dist-info/entry_points.txt +2 -0
  41. arg2p-0.19.0.dist-info/top_level.txt +1 -0
arg2p/__init__.py ADDED
@@ -0,0 +1,63 @@
1
+ """
2
+ Arg2P from Python: structured argumentation in Prolog (ASPIC+, ABA, abstract frameworks, ...), the library of the JVM
3
+ run through JPype, on the Java runtime the package bundles.
4
+
5
+ >>> from arg2p import Arg2p
6
+ >>> Arg2p.of("r1 : [] => a.\nr2 : [] => -a.\nsup(r1, r2).").answer("a").in_statements
7
+ ['a']
8
+ """
9
+
10
+ from .arg2p import Answer, Arg2p, Arg2pError, Builder, Causality, Solution, Solutions
11
+ from .flags import (
12
+ ConflictModel,
13
+ Flags,
14
+ GraphBuilder,
15
+ GraphExtension,
16
+ GraphLanguage,
17
+ OrderingComparator,
18
+ OrderingPrinciple,
19
+ Profile,
20
+ Semantics,
21
+ StatementLabelling,
22
+ )
23
+ from .model import Argument, Attack, Graph, LabelledArgument, Support
24
+ from .parallel import EvaluationResponse, LocalCluster, Response, ThreadedCluster
25
+ from .values import Struct, Value, Var
26
+
27
+ try:
28
+ from ._version import __version__
29
+ except ImportError: # a package built without Gradle
30
+ __version__ = "0.0.0"
31
+
32
+ __all__ = [
33
+ "Answer",
34
+ "Arg2p",
35
+ "Arg2pError",
36
+ "Argument",
37
+ "Attack",
38
+ "Builder",
39
+ "Causality",
40
+ "ConflictModel",
41
+ "EvaluationResponse",
42
+ "Flags",
43
+ "Graph",
44
+ "GraphBuilder",
45
+ "GraphExtension",
46
+ "GraphLanguage",
47
+ "LabelledArgument",
48
+ "LocalCluster",
49
+ "OrderingComparator",
50
+ "OrderingPrinciple",
51
+ "Profile",
52
+ "Response",
53
+ "Semantics",
54
+ "Solution",
55
+ "Solutions",
56
+ "StatementLabelling",
57
+ "Struct",
58
+ "Support",
59
+ "ThreadedCluster",
60
+ "Value",
61
+ "Var",
62
+ "__version__",
63
+ ]
arg2p/__main__.py ADDED
@@ -0,0 +1,49 @@
1
+ """The labels of the statements of a theory, from the command line: python -m arg2p theory.pl [-q query] ..."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+ from typing import Optional
8
+
9
+ from .arg2p import Arg2p, Arg2pError
10
+ from .flags import Flags, Profile, Semantics
11
+ from .values import text
12
+
13
+
14
+ def main(argv: Optional[list[str]] = None) -> int:
15
+ parser = argparse.ArgumentParser(prog="arg2p", description="Evaluates a theory of arg2p.")
16
+ parser.add_argument("theory", help="the file of the theory (- for the standard input)")
17
+ parser.add_argument("-q", "--query", action="append", help="a statement to evaluate (all of them by default)")
18
+ parser.add_argument("-p", "--profile", choices=[it.value for it in Profile], help="the profile of the theory")
19
+ parser.add_argument("-s", "--semantics", choices=[it.value for it in Semantics], help="the semantics")
20
+ arguments = parser.parse_args(argv)
21
+ theory = sys.stdin.read() if arguments.theory == "-" else open(arguments.theory, encoding="utf-8").read()
22
+ flags = Flags.defaults()
23
+ if arguments.profile:
24
+ flags.graph_build_mode(Profile(arguments.profile))
25
+ if arguments.semantics:
26
+ flags.argument_labelling_mode(Semantics(arguments.semantics))
27
+ try:
28
+ arg2p = Arg2p.of(theory, flags)
29
+ if arguments.query:
30
+ for query in arguments.query:
31
+ answer = arg2p.answer(query)
32
+ print(
33
+ f"{query}: in {text(answer.in_statements)}, out {text(answer.out_statements)}, "
34
+ f"und {text(answer.und_statements)}"
35
+ )
36
+ else:
37
+ for index, graph in enumerate(arg2p.graphs()):
38
+ if index:
39
+ print()
40
+ for label in ("in", "out", "und"):
41
+ print(f"{label}: {', '.join(sorted(graph.conclusions(label)))}")
42
+ except Arg2pError as error:
43
+ print(f"arg2p: {error}", file=sys.stderr)
44
+ return 1
45
+ return 0
46
+
47
+
48
+ if __name__ == "__main__":
49
+ sys.exit(main())
arg2p/_jvm.py ADDED
@@ -0,0 +1,145 @@
1
+ """The JVM running arg2p: started once, at the first use, with the jars of the package, on the Java runtime it
2
+ bundles (that of the system, for a package without one)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import glob
7
+ import os
8
+ import sys
9
+ import threading
10
+ from typing import Any, Callable, TypeVar
11
+
12
+ import jpype
13
+
14
+ T = TypeVar("T")
15
+
16
+ _lock = threading.Lock()
17
+
18
+ #: The jars of the package, copied there by ``./gradlew :python:pythonJars``
19
+ JARS = os.path.join(os.path.dirname(__file__), "jars")
20
+
21
+ #: The Java runtime the package bundles, made there by ``./gradlew :python:pythonRuntime``
22
+ RUNTIME = os.path.join(os.path.dirname(__file__), "jre")
23
+
24
+ #: The stack of the threads of the JVM: resolution goes deep (the tests of arg2p run with the same)
25
+ STACK = os.environ.get("ARG2P_STACK", "512m")
26
+
27
+
28
+ def classpath() -> list[str]:
29
+ """The jars of arg2p: those of ``ARG2P_CLASSPATH`` if set, those of the package otherwise."""
30
+ stated = os.environ.get("ARG2P_CLASSPATH")
31
+ if stated:
32
+ return stated.split(os.pathsep)
33
+ jars = sorted(glob.glob(os.path.join(JARS, "*.jar")))
34
+ if not jars:
35
+ raise RuntimeError(
36
+ "No jar of arg2p in " + JARS + ": copy them with ./gradlew :python:pythonJars, "
37
+ "or set ARG2P_CLASSPATH"
38
+ )
39
+ return jars
40
+
41
+
42
+ def jvm_path() -> str:
43
+ """
44
+ The library of the JVM: that of ``ARG2P_JVM`` if set, of the runtime the package bundles if it does, of the system
45
+ otherwise (JAVA_HOME, or where JPype finds one).
46
+ """
47
+ stated = os.environ.get("ARG2P_JVM")
48
+ if stated:
49
+ return stated
50
+ library = {"win32": ("bin", "server", "jvm.dll"), "darwin": ("lib", "server", "libjvm.dylib")}.get(
51
+ sys.platform, ("lib", "server", "libjvm.so")
52
+ )
53
+ bundled = os.path.join(RUNTIME, *library)
54
+ return bundled if os.path.exists(bundled) else jpype.getDefaultJVMPath()
55
+
56
+
57
+ def start() -> None:
58
+ """Starts the JVM, unless it runs already (started by another library: the jars of arg2p must be on its path)."""
59
+ with _lock:
60
+ if not jpype.isJVMStarted():
61
+ jpype.startJVM(
62
+ jvm_path(), f"-Xss{STACK}", "-Dslf4j.internal.verbosity=ERROR", classpath=classpath(), convertStrings=True
63
+ )
64
+
65
+
66
+ def jclass(name: str) -> Any:
67
+ """The Java class [name], the JVM started."""
68
+ start()
69
+ return jpype.JClass(name)
70
+
71
+
72
+ @jpype.JImplements("java.lang.Runnable", deferred=True)
73
+ class _Task:
74
+ def __init__(self, function: Callable[[], Any]) -> None:
75
+ self.function = function
76
+ self.value: Any = None
77
+ self.error: BaseException | None = None
78
+
79
+ @jpype.JOverride
80
+ def run(self) -> None:
81
+ try:
82
+ self.value = self.function()
83
+ except BaseException as error: # noqa: BLE001 - raised again by the caller
84
+ self.error = error
85
+
86
+
87
+ @jpype.JImplements("java.util.concurrent.ThreadFactory", deferred=True)
88
+ class _Threads:
89
+ @jpype.JOverride
90
+ def newThread(self, runnable: Any) -> Any:
91
+ # daemons, not to hold the JVM when Python exits; their stack is the one of -Xss (see STACK)
92
+ thread = jpype.JClass("java.lang.Thread")(runnable, _THREAD)
93
+ thread.setDaemon(True)
94
+ return thread
95
+
96
+
97
+ _THREAD = "arg2p"
98
+ _pool: Any = None
99
+
100
+
101
+ def _threads() -> Any:
102
+ """The threads running arg2p, made as calls need them and kept for the next ones (a minute idle)."""
103
+ global _pool
104
+ with _lock:
105
+ if _pool is None:
106
+ _pool = jpype.JClass("java.util.concurrent.Executors").newCachedThreadPool(_Threads())
107
+ return _pool
108
+
109
+
110
+ def run(function: Callable[[], T]) -> T:
111
+ """The result of [function], run on a thread of the JVM, whose stack resolution needs (see STACK)."""
112
+ start()
113
+ if str(jpype.JClass("java.lang.Thread").currentThread().getName()) == _THREAD:
114
+ return function() # already on one
115
+ task = _Task(function)
116
+ _threads().submit(task).get()
117
+ if task.error is not None:
118
+ raise task.error
119
+ return task.value
120
+
121
+
122
+ @jpype.JImplements("kotlin.jvm.functions.Function1", deferred=True)
123
+ class _Function:
124
+ def __init__(self, function: Callable[[Any], Any]) -> None:
125
+ self.function = function
126
+
127
+ @jpype.JOverride
128
+ def invoke(self, value: Any) -> Any:
129
+ return self.function(value)
130
+
131
+
132
+ def function1(function: Callable[[Any], Any]) -> Any:
133
+ """[function] as a Kotlin function of one argument returning Unit."""
134
+ unit = jclass("kotlin.Unit").INSTANCE
135
+
136
+ def invoke(value: Any) -> Any:
137
+ function(value)
138
+ return unit
139
+
140
+ return _Function(invoke)
141
+
142
+
143
+ def jarray(name: str, values: list[Any]) -> Any:
144
+ """A Java array of the class [name], of [values]."""
145
+ return jpype.JArray(jclass(name))(values)
arg2p/_version.py ADDED
@@ -0,0 +1,2 @@
1
+ # Generated by the build (./gradlew :python:pythonVersion): the version of arg2p
2
+ __version__ = "0.19.0"
arg2p/arg2p.py ADDED
@@ -0,0 +1,271 @@
1
+ """A solver of arg2p: its answers, graphs and solutions, and the causality tests on its theory."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Any, Callable, Iterator, Optional
7
+
8
+ import jpype
9
+
10
+ from . import _jvm
11
+ from .flags import Flags
12
+ from .model import Graph, graph_from_jvm
13
+ from .values import Value, from_jvm
14
+
15
+ _API = "it.unibo.tuprolog.argumentation"
16
+
17
+
18
+ class Arg2pError(Exception):
19
+ """An evaluation halted by an error: a theory its profile refuses, a goal that cannot be parsed, ..."""
20
+
21
+
22
+ def call(function: Callable[[], Any]) -> Any:
23
+ """The result of [function], run on a thread of the JVM, an exception of arg2p raised as an Arg2pError."""
24
+
25
+ def guarded() -> Any:
26
+ try:
27
+ return function()
28
+ except jpype.JException as error:
29
+ if error.getClass().getName() == f"{_API}.Arg2pException":
30
+ raise Arg2pError(str(error.getMessage())) from None
31
+ raise
32
+
33
+ return _jvm.run(guarded)
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class Answer:
38
+ """The statements for a query labelled in, out and und (see Arg2p.answer)."""
39
+
40
+ in_statements: list[Value] = field(default_factory=list)
41
+ out_statements: list[Value] = field(default_factory=list)
42
+ und_statements: list[Value] = field(default_factory=list)
43
+ # the answer of the library of the JVM, for label
44
+ _answer: Any = field(default=None, compare=False, repr=False)
45
+
46
+ def label(self, statement: str) -> Optional[str]:
47
+ """The label of [statement], Prolog text: in, out or und, None for a statement the query did not label."""
48
+ if self._answer is None:
49
+ raise ValueError("Not the answer of a solver")
50
+ label = call(lambda: self._answer.label(statement))
51
+ return None if label is None else str(label)
52
+
53
+
54
+ class Solution:
55
+ """A solution of a goal: yes, no or halt (an error), with the values a yes solution binds to its variables."""
56
+
57
+ def __init__(self, solution: Any) -> None:
58
+ self._solution = solution
59
+
60
+ @property
61
+ def is_yes(self) -> bool:
62
+ return bool(self._solution.isYes())
63
+
64
+ @property
65
+ def is_no(self) -> bool:
66
+ return bool(self._solution.isNo())
67
+
68
+ @property
69
+ def is_halt(self) -> bool:
70
+ return bool(self._solution.isHalt())
71
+
72
+ @property
73
+ def query(self) -> str:
74
+ """The goal, as a yes solution solves it (its variables bound), otherwise as it was asked, as Prolog text."""
75
+ return str(self._solution.getQuery())
76
+
77
+ @property
78
+ def error(self) -> Optional[str]:
79
+ """The error of a halt solution."""
80
+ error = self._solution.getError()
81
+ return None if error is None else str(error)
82
+
83
+ @property
84
+ def variables(self) -> list[str]:
85
+ """The variables of the goal a yes solution binds."""
86
+ return [str(name) for name in self._solution.getVariables()]
87
+
88
+ @property
89
+ def bindings(self) -> dict[str, Value]:
90
+ """The values a yes solution binds to the variables of the goal, by their names."""
91
+ return {str(entry.getKey()): from_jvm(entry.getValue()) for entry in self._solution.getBindings().entrySet()}
92
+
93
+ def value(self, name: str) -> Optional[Value]:
94
+ """The value a yes solution binds to the variable of the goal named [name]."""
95
+ value = self._solution.value(name)
96
+ return None if value is None else from_jvm(value)
97
+
98
+ def text(self, name: str) -> Optional[str]:
99
+ """The value a yes solution binds to the variable of the goal named [name], as Prolog text."""
100
+ written = self._solution.text(name)
101
+ return None if written is None else str(written)
102
+
103
+ def __repr__(self) -> str:
104
+ return str(self._solution.toString())
105
+
106
+
107
+ class Solutions:
108
+ """The solutions of a goal, computed one at a time as they are asked for: with has_next and next, or iterated."""
109
+
110
+ def __init__(self, solutions: Any) -> None:
111
+ self._solutions = solutions
112
+
113
+ def has_next(self) -> bool:
114
+ return bool(_jvm.run(self._solutions.hasNext))
115
+
116
+ def next(self) -> Solution:
117
+ return Solution(_jvm.run(self._solutions.next))
118
+
119
+ def to_list(self, limit: Optional[int] = None) -> list[Solution]:
120
+ """The solutions left, at most [limit] of them."""
121
+ return [Solution(it) for it in _jvm.run(lambda: self._solutions.toList(2**31 - 1 if limit is None else limit))]
122
+
123
+ def __iter__(self) -> Iterator[Solution]:
124
+ return self
125
+
126
+ def __next__(self) -> Solution:
127
+ # the next solution, if there is one, in a single call
128
+ found = _jvm.run(lambda: self._solutions.next() if self._solutions.hasNext() else None)
129
+ if found is None:
130
+ raise StopIteration
131
+ return Solution(found)
132
+
133
+
134
+ class Causality:
135
+ """
136
+ Whether a statement of the theory of a solver causes another, on the grounded labelling of the theory and of its
137
+ interventions: the NESS test, as published (Pisano, Prakken, Sartor, Liepina; ICAIL 2025) and revised, and the
138
+ but-for test. The statements are Prolog text; an intervention is a list of statements.
139
+ """
140
+
141
+ def __init__(self, causality: Any) -> None:
142
+ self._causality = causality
143
+
144
+ def ness(self, cause: str, effect: str) -> Optional[Value]:
145
+ """Whether [cause] is a NESS cause of [effect] (the revised condition): the intervention, None if it is not."""
146
+ return _optional(call(lambda: self._causality.ness(cause, effect)))
147
+
148
+ def ness_original(self, cause: str, effect: str) -> Optional[Value]:
149
+ """Whether [cause] is a NESS cause of [effect] as published: the intervention, None if it is not."""
150
+ return _optional(call(lambda: self._causality.nessOriginal(cause, effect)))
151
+
152
+ def ness_intervention(self, intervention: str, effect: str) -> bool:
153
+ """Whether [intervention], a list of statements, meets the revised NESS condition for [effect]."""
154
+ return bool(call(lambda: self._causality.nessIntervention(intervention, effect)))
155
+
156
+ def ness_original_intervention(self, intervention: str, effect: str) -> bool:
157
+ """Whether [intervention], a list of statements, meets the NESS condition for [effect] as published."""
158
+ return bool(call(lambda: self._causality.nessOriginalIntervention(intervention, effect)))
159
+
160
+ def but_for(self, cause: str, effect: str) -> bool:
161
+ """Whether [cause] is a but-for cause of [effect]."""
162
+ return bool(call(lambda: self._causality.butFor(cause, effect)))
163
+
164
+
165
+ def _optional(value: Any) -> Optional[Value]:
166
+ return None if value is None else from_jvm(value)
167
+
168
+
169
+ class Arg2p:
170
+ """
171
+ A solver of arg2p for a theory, as Prolog text, with its flags: it answers queries (answer), gives the graph of
172
+ each labelling (graphs), solves Prolog goals (solve) and tests causality (causality).
173
+
174
+ >>> Arg2p.of("r1 : [] => a.\\nr2 : [] => -a.\\nsup(r1, r2).").answer("a").in_statements
175
+ ['a']
176
+
177
+ The context of the solver keeps a branch for every stage of a resolution, numbered from 0: active_branch is the
178
+ last one, graph_at the graph of any of them, and context_facts what a branch stores.
179
+ """
180
+
181
+ def __init__(self, arg2p: Any) -> None:
182
+ self._arg2p = arg2p
183
+ self.causality = Causality(arg2p.getCausality())
184
+
185
+ @staticmethod
186
+ def of(theory: str = "", flags: Optional[Flags] = None) -> Arg2p:
187
+ """A solver of arg2p for [theory], Prolog text, with [flags] (Flags.defaults() otherwise)."""
188
+ return Arg2p.builder().theory(theory).flags(flags or Flags.defaults()).build()
189
+
190
+ @staticmethod
191
+ def builder() -> Builder:
192
+ """A solver of arg2p, as it is described."""
193
+ return Builder()
194
+
195
+ @staticmethod
196
+ def version() -> str:
197
+ """The version of arg2p, the released one for a release."""
198
+ return str(_jvm.jclass(f"{_API}.Arg2p").version())
199
+
200
+ @property
201
+ def theory(self) -> str:
202
+ """The theory, as Prolog text."""
203
+ return str(self._arg2p.getTheory())
204
+
205
+ def answer(self, query: str) -> Answer:
206
+ """The statements for [query], Prolog text, labelled in, out and und (raises Arg2pError if it halts)."""
207
+ answer = call(lambda: self._arg2p.answer(query))
208
+ return Answer(
209
+ [from_jvm(it) for it in answer.getInStatements()],
210
+ [from_jvm(it) for it in answer.getOutStatements()],
211
+ [from_jvm(it) for it in answer.getUndStatements()],
212
+ answer,
213
+ )
214
+
215
+ def graphs(self) -> list[Graph]:
216
+ """The graph of each labelling of the theory: several for a semantics with several (preferred, stable, ...)."""
217
+ return call(lambda: [graph_from_jvm(graph) for graph in self._arg2p.graphs()])
218
+
219
+ def graph(self) -> Graph:
220
+ """The graph of the last resolution: the one of its last stage."""
221
+ return call(lambda: graph_from_jvm(self._arg2p.graph()))
222
+
223
+ def graph_at(self, branch: int) -> Graph:
224
+ """The graph of the context branch [branch]."""
225
+ return call(lambda: graph_from_jvm(self._arg2p.graphAt(branch)))
226
+
227
+ @property
228
+ def active_branch(self) -> int:
229
+ """The context branch of the last stage of the last resolution."""
230
+ return int(call(self._arg2p.getActiveBranch))
231
+
232
+ def solve(self, goal: str) -> Solutions:
233
+ """The solutions of [goal], Prolog text, computed one at a time as they are asked for."""
234
+ return Solutions(call(lambda: self._arg2p.solve(goal)))
235
+
236
+ def context_facts(self, branch: int, goal: str) -> Solutions:
237
+ """What the context branch [branch] stores matching [goal], Prolog text: the solutions of context_check/2."""
238
+ return Solutions(call(lambda: self._arg2p.contextFacts(branch, goal)))
239
+
240
+ def add(self, theory: str) -> None:
241
+ """Adds the clauses of [theory], Prolog text, to the theory."""
242
+ call(lambda: self._arg2p.add(theory))
243
+
244
+ def load(self, theory: str) -> None:
245
+ """Replaces the theory with [theory], Prolog text."""
246
+ call(lambda: self._arg2p.load(theory))
247
+
248
+
249
+ class Builder:
250
+ """A solver of arg2p, as it is described: its theory, its flags, and where what it prints goes."""
251
+
252
+ def __init__(self) -> None:
253
+ self._builder = _jvm.jclass(f"{_API}.Arg2p").builder()
254
+
255
+ def theory(self, theory: str) -> Builder:
256
+ """The theory, as Prolog text."""
257
+ self._builder.theory(theory)
258
+ return self
259
+
260
+ def flags(self, flags: Flags) -> Builder:
261
+ """The flags (Flags.defaults() otherwise)."""
262
+ self._builder.flags(flags.jvm())
263
+ return self
264
+
265
+ def output(self, consumer: Callable[[str], Any]) -> Builder:
266
+ """The function receiving what the solver prints (the standard output otherwise)."""
267
+ self._builder.output(_jvm.function1(lambda printed: consumer(str(printed))))
268
+ return self
269
+
270
+ def build(self) -> Arg2p:
271
+ return Arg2p(call(self._builder.build))
arg2p/flags.py ADDED
@@ -0,0 +1,224 @@
1
+ """The flags of a solver (see the flags reference of arg2p), and the values they take."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import Enum
6
+ from typing import Any
7
+
8
+ from . import _jvm
9
+
10
+ _API = "it.unibo.tuprolog.argumentation"
11
+
12
+
13
+ class _FlagValue(Enum):
14
+ """A value of a flag: [value] is the atom stating it in a theory."""
15
+
16
+ def jvm(self) -> Any:
17
+ """The value of the library of the JVM."""
18
+ return getattr(_jvm.jclass(f"{_API}.{type(self).__name__}"), self.name)
19
+
20
+
21
+ class Profile(_FlagValue):
22
+ """The profile of a theory (graphBuildMode): the flags its definition needs, preset."""
23
+
24
+ FREE = "free"
25
+ ASPIC_PLUS = "aspic_plus"
26
+ ASPIC = "aspic"
27
+ ASPIC_DEFEASIBLE = "aspic_defeasible"
28
+ META = "meta"
29
+ ABA = "aba"
30
+ ABA_PLUS = "aba_plus"
31
+ ABSTRACT = "abstract"
32
+
33
+
34
+ class Semantics(_FlagValue):
35
+ """The semantics of the argument labelling (argumentLabellingMode)."""
36
+
37
+ CONFLICT_FREE = "conflictfree"
38
+ ADMISSIBLE = "admissible"
39
+ STRONGLY_ADMISSIBLE = "stronglyadmissible"
40
+ GROUNDED = "grounded"
41
+ COMPLETE = "complete"
42
+ PREFERRED = "preferred"
43
+ SEMI_STABLE = "semistable"
44
+ STABLE = "stable"
45
+ IDEAL = "ideal"
46
+ EAGER = "eager"
47
+ NAIVE = "naive"
48
+ STAGE = "stage"
49
+ CF2 = "cf2"
50
+ STAGE2 = "stage2"
51
+ BP_GROUNDED = "bp_grounded"
52
+ BP_GROUNDED_PARTIAL = "bp_grounded_partial"
53
+ BP_GROUNDED_COMPLETE = "bp_grounded_complete"
54
+
55
+
56
+ class StatementLabelling(_FlagValue):
57
+ """The labelling of the statements from that of the arguments (statementLabellingMode)."""
58
+
59
+ STATEMENT = "statement"
60
+ STATEMENT_PASS_THROUGH = "statement_pass_through"
61
+ STATEMENT_BINARY = "statement_binary"
62
+
63
+
64
+ class OrderingPrinciple(_FlagValue):
65
+ """The ordering principle of the preferences (orderingPrinciple)."""
66
+
67
+ LAST = "last"
68
+ WEAKEST = "weakest"
69
+
70
+
71
+ class OrderingComparator(_FlagValue):
72
+ """The comparator of the sets of rules or premises (orderingComparator)."""
73
+
74
+ ELITIST = "elitist"
75
+ DEMOCRAT = "democrat"
76
+ NORMAL = "normal"
77
+
78
+
79
+ class GraphExtension(_FlagValue):
80
+ """An extension of the graph (graphExtension)."""
81
+
82
+ REBUT_RESTRICTION = "rebutRestriction"
83
+ BP = "bp"
84
+ STANDARD_PREF = "standardPref"
85
+ DEFEASIBLE_PREF = "defeasiblePref"
86
+ DEFEASIBLE_ALL_PREF = "defeasibleAllPref"
87
+ ABA_PREF = "abaPref"
88
+
89
+
90
+ class GraphLanguage(_FlagValue):
91
+ """A language extending the one of arg2p (graphLanguage)."""
92
+
93
+ ASPIC_LANGUAGE = "aspicLanguage"
94
+ ABA_LANGUAGE = "abaLanguage"
95
+ DEFEASIBLE_LANGUAGE = "defeasibleLanguage"
96
+
97
+
98
+ class GraphBuilder(_FlagValue):
99
+ """The module building the graph (graphBuilder): from the rules, or from the arguments and attacks."""
100
+
101
+ RULE_GRAPH = "ruleGraph"
102
+ ABSTRACT_GRAPH = "abstractGraph"
103
+
104
+
105
+ class ConflictModel(_FlagValue):
106
+ """The conflicts beyond those a theory declares (conflictModel)."""
107
+
108
+ BUILTIN = "builtin"
109
+ DECLARED = "declared"
110
+
111
+
112
+ def _array(cls: type, values: tuple) -> Any:
113
+ return _jvm.jarray(f"{_API}.{cls.__name__}", [value.jvm() for value in values])
114
+
115
+
116
+ class Flags:
117
+ """
118
+ The flags of a solver, as the facts stating them: defaults() (those of arg2p), none() (only those the theory
119
+ states), or parse(text). A flag set replaces what the flags stated for it; the theory may state flags as well,
120
+ which override these. The profile of the theory (graph_build_mode) presets the flags its definition needs.
121
+ """
122
+
123
+ def __init__(self, flags: Any) -> None:
124
+ self._flags = flags
125
+
126
+ @staticmethod
127
+ def defaults() -> Flags:
128
+ """The flags of arg2p by default."""
129
+ return Flags(_jvm.jclass(f"{_API}.Flags").defaults())
130
+
131
+ @staticmethod
132
+ def none() -> Flags:
133
+ """No flags: the solver has only those the theory states, the others are the defaults of their modules."""
134
+ return Flags(_jvm.jclass(f"{_API}.Flags").none())
135
+
136
+ @staticmethod
137
+ def parse(text: str) -> Flags:
138
+ """The flags stated by the facts of [text], Prolog text (argumentLabellingMode(preferred). queryMode.)."""
139
+ return Flags(_jvm.jclass(f"{_API}.Flags").parse(text))
140
+
141
+ def jvm(self) -> Any:
142
+ """The flags of the library of the JVM."""
143
+ return self._flags
144
+
145
+ def graph_build_mode(self, profile: Profile) -> Flags:
146
+ """The profile of the theory."""
147
+ return self._set(self._flags.graphBuildMode(profile.jvm()))
148
+
149
+ def argument_labelling_mode(self, semantics: Semantics) -> Flags:
150
+ """The semantics of the argument labelling."""
151
+ return self._set(self._flags.argumentLabellingMode(semantics.jvm()))
152
+
153
+ def statement_labelling_mode(self, labelling: StatementLabelling) -> Flags:
154
+ """The labelling of the statements."""
155
+ return self._set(self._flags.statementLabellingMode(labelling.jvm()))
156
+
157
+ def ordering_principle(self, principle: OrderingPrinciple) -> Flags:
158
+ """The ordering principle of the preferences."""
159
+ return self._set(self._flags.orderingPrinciple(principle.jvm()))
160
+
161
+ def ordering_comparator(self, comparator: OrderingComparator) -> Flags:
162
+ """The comparator of the preferences."""
163
+ return self._set(self._flags.orderingComparator(comparator.jvm()))
164
+
165
+ def graph_extensions(self, *extensions: GraphExtension) -> Flags:
166
+ """The extensions of the graph: none for no extension."""
167
+ return self._set(self._flags.graphExtensions(_array(GraphExtension, extensions)))
168
+
169
+ def graph_language(self, *languages: GraphLanguage) -> Flags:
170
+ """The languages extending the one of arg2p: none for no language."""
171
+ return self._set(self._flags.graphLanguage(_array(GraphLanguage, languages)))
172
+
173
+ def graph_builder(self, builder: GraphBuilder) -> Flags:
174
+ """The module building the graph."""
175
+ return self._set(self._flags.graphBuilder(builder.jvm()))
176
+
177
+ def conflict_model(self, model: ConflictModel) -> Flags:
178
+ """The conflicts beyond those the theory declares."""
179
+ return self._set(self._flags.conflictModel(model.jvm()))
180
+
181
+ def modules_path(self, path: str) -> Flags:
182
+ """Where the modules loaded by name are looked for."""
183
+ return self._set(self._flags.modulesPath(path))
184
+
185
+ def query_mode(self, on: bool) -> Flags:
186
+ """Whether the graph is built on the arguments the query depends on alone."""
187
+ return self._set(self._flags.queryMode(on))
188
+
189
+ def auto_transposition(self, on: bool) -> Flags:
190
+ """Whether the strict rules are transposed."""
191
+ return self._set(self._flags.autoTransposition(on))
192
+
193
+ def prolog_strict_compatibility(self, on: bool) -> Flags:
194
+ """Whether the theory is read as strict Prolog (no rules of arg2p)."""
195
+ return self._set(self._flags.prologStrictCompatibility(on))
196
+
197
+ def meta_conflicts(self, on: bool) -> Flags:
198
+ """Whether the conflicts are arguments as well."""
199
+ return self._set(self._flags.metaConflicts(on))
200
+
201
+ def meta_rules(self, on: bool) -> Flags:
202
+ """Whether the rules are arguments as well."""
203
+ return self._set(self._flags.metaRules(on))
204
+
205
+ def debug(self, on: bool) -> Flags:
206
+ """Whether the modules print their debug output."""
207
+ return self._set(self._flags.debug(on))
208
+
209
+ def _set(self, flags: Any) -> Flags:
210
+ self._flags = flags
211
+ return self
212
+
213
+ def __str__(self) -> str:
214
+ """The flags as the facts stating them, Prolog text."""
215
+ return str(self._flags.toString())
216
+
217
+ def __repr__(self) -> str:
218
+ return f"Flags({str(self)!r})"
219
+
220
+ def __eq__(self, other: object) -> bool:
221
+ return isinstance(other, Flags) and bool(self._flags.equals(other._flags))
222
+
223
+ def __hash__(self) -> int:
224
+ return int(self._flags.hashCode())
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
arg2p/model.py ADDED
@@ -0,0 +1,102 @@
1
+ """The argumentation graph of a labelling, as plain data: its arguments, their labels, attacks and supports."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Any, Optional
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class Argument:
11
+ """
12
+ An argument: its rules, its top rule (none for a premise) and its conclusion, with the arguments it is built on
13
+ (supports), identified as A0, A1, ... in the graph (identifier); descriptor writes it as A2 : A0,r1 : c.
14
+ """
15
+
16
+ identifier: str
17
+ descriptor: str
18
+ conclusion: str
19
+ top_rule: str
20
+ rules: tuple[str, ...] = ()
21
+ supports: tuple[Argument, ...] = field(default=(), repr=False)
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class Attack:
26
+ """[attacker] attacks [target], with its type (rebut, undermine, undercut, ...) and the sub-argument it hits."""
27
+
28
+ attacker: Argument
29
+ target: Argument
30
+ type: Optional[str] = None
31
+ on: Optional[Argument] = None
32
+
33
+
34
+ @dataclass(frozen=True)
35
+ class Support:
36
+ """[supporter] is an argument [supported] is built on."""
37
+
38
+ supporter: Argument
39
+ supported: Argument
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class LabelledArgument:
44
+ """An argument with its label: in, out, und (na for an argument the labelling does not label)."""
45
+
46
+ argument: Argument
47
+ label: str
48
+
49
+
50
+ @dataclass(frozen=True)
51
+ class Graph:
52
+ """An argumentation graph: its arguments, the attacks and supports between them, and the label of each argument."""
53
+
54
+ arguments: tuple[Argument, ...]
55
+ attacks: tuple[Attack, ...] = ()
56
+ supports: tuple[Support, ...] = ()
57
+ labellings: tuple[LabelledArgument, ...] = ()
58
+
59
+ def label(self, argument: Argument) -> Optional[str]:
60
+ """The label of [argument]: in, out, und, or na for an argument the labelling does not label."""
61
+ return next((labelled.label for labelled in self.labellings if labelled.argument == argument), None)
62
+
63
+ def labelled(self, label: str) -> list[Argument]:
64
+ """The arguments labelled [label] (in, out, und)."""
65
+ return [labelled.argument for labelled in self.labellings if labelled.label == label]
66
+
67
+ def conclusions(self, label: str) -> list[str]:
68
+ """The conclusions of the arguments labelled [label] (in, out, und), each once."""
69
+ return list(dict.fromkeys(argument.conclusion for argument in self.labelled(label)))
70
+
71
+
72
+ def graph_from_jvm(graph: Any) -> Graph:
73
+ """The graph of [graph], a Graph of the library of the JVM."""
74
+ arguments: dict[str, Argument] = {}
75
+
76
+ def argument(of: Any) -> Argument:
77
+ identifier = str(of.getIdentifier())
78
+ if identifier not in arguments:
79
+ arguments[identifier] = Argument(
80
+ identifier,
81
+ str(of.getDescriptor()),
82
+ str(of.getConclusion()),
83
+ str(of.getTopRule()),
84
+ tuple(str(rule) for rule in of.getRules()),
85
+ tuple(argument(support) for support in of.getSupports()),
86
+ )
87
+ return arguments[identifier]
88
+
89
+ return Graph(
90
+ tuple(argument(of) for of in graph.getArguments()),
91
+ tuple(
92
+ Attack(
93
+ argument(of.getAttacker()),
94
+ argument(of.getTarget()),
95
+ None if of.getType() is None else str(of.getType()),
96
+ None if of.getOn() is None else argument(of.getOn()),
97
+ )
98
+ for of in graph.getAttacks()
99
+ ),
100
+ tuple(Support(argument(of.getSupporter()), argument(of.getSupported())) for of in graph.getSupports()),
101
+ tuple(LabelledArgument(argument(of.getArgument()), str(of.getLabel())) for of in graph.getLabellings()),
102
+ )
arg2p/parallel.py ADDED
@@ -0,0 +1,160 @@
1
+ """The parallel solver of arg2p: clusters of workers, run in turn (LocalCluster) or on the threads of the JVM
2
+ (ThreadedCluster), federated with other clusters (of the JVM, of Node.js, of browsers) over WebSockets, by address or
3
+ found on the local network."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import Any, Optional
9
+
10
+ from . import _jvm
11
+ from .arg2p import call
12
+ from .flags import Flags
13
+
14
+ _CLUSTER = "it.unibo.tuprolog.argumentation.parallel.cluster"
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class Response:
19
+ """
20
+ An argument for the statement evaluated (claim, the statement), with the argument itself in query_chain (empty for
21
+ a statement no argument concludes), as Prolog text.
22
+ """
23
+
24
+ claim: str
25
+ query_chain: tuple[str, ...] = ()
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class EvaluationResponse:
30
+ """The arguments for a statement, by label."""
31
+
32
+ in_args: list[Response] = field(default_factory=list)
33
+ out_args: list[Response] = field(default_factory=list)
34
+ und_args: list[Response] = field(default_factory=list)
35
+
36
+
37
+ def _response(response: Any) -> EvaluationResponse:
38
+ def responses(of: Any) -> list[Response]:
39
+ return [Response(str(it.getClaim()), tuple(str(argument) for argument in it.getQueryChain())) for it in of]
40
+
41
+ return EvaluationResponse(responses(response.getInArgs()), responses(response.getOutArgs()), responses(response.getUndArgs()))
42
+
43
+
44
+ def _arguments(*given: Any, defaults: tuple[Any, ...]) -> list[Any]:
45
+ """
46
+ The arguments of a constructor of the JVM, up to the last one given: those left out after it take its defaults,
47
+ those left out before it [defaults] (as the constructor has them)
48
+ """
49
+ last = max((i for i, it in enumerate(given) if it is not None), default=-1)
50
+ return [given[i] if given[i] is not None else defaults[i] for i in range(last + 1)]
51
+
52
+
53
+ def _parts(parts: Optional[int]) -> Any:
54
+ return None if parts is None else _jvm.jclass("java.lang.Integer").valueOf(parts)
55
+
56
+
57
+ class LocalCluster:
58
+ """
59
+ A distributed solver whose nodes run in turn, on the thread of the caller: the same messages as on a cluster, in a
60
+ deterministic order. The theory is split into at most [parts] partitions if given, one for each group of rules
61
+ supporting one another otherwise; the graph is labelled as the standard solver does with [flags].
62
+ """
63
+
64
+ def __init__(self, parts: Optional[int] = None, flags: Optional[Flags] = None) -> None:
65
+ arguments = _arguments(_parts(parts), flags and flags.jvm(), defaults=(None, None))
66
+ self._cluster = call(lambda: _jvm.jclass(f"{_CLUSTER}.LocalCluster")(*arguments))
67
+
68
+ def load(self, theory: str) -> None:
69
+ """Replaces the theory with [theory], as Prolog text."""
70
+ call(lambda: self._cluster.load(theory))
71
+
72
+ def add(self, clause: str) -> None:
73
+ """Adds [clause], as Prolog text, to the theory."""
74
+ call(lambda: self._cluster.add(clause))
75
+
76
+ def reset(self) -> None:
77
+ """Forgets the theory."""
78
+ call(self._cluster.reset)
79
+
80
+ def solve(self, statement: str) -> EvaluationResponse:
81
+ """The arguments for [statement], as Prolog text, by label (Arg2pError for one that is not a statement)."""
82
+ return call(lambda: _response(self._cluster.solve(statement)))
83
+
84
+
85
+ class ThreadedCluster:
86
+ """
87
+ A distributed solver on the threads of the JVM: each node handles its messages one at a time, the nodes in parallel
88
+ on [threads] threads (as many as the processors by default). The theory is split into at most [parts] partitions
89
+ if given, as many as the workers of the cluster and of the sites it is federated with otherwise; the graph is
90
+ labelled as the standard solver does with [flags]. With query mode (the default) the graph is built on the
91
+ arguments the statements evaluated depend on, as the standard solver builds it. [lends] tells whether the cluster
92
+ lends its workers to the others (it does by default).
93
+ """
94
+
95
+ def __init__(
96
+ self,
97
+ threads: Optional[int] = None,
98
+ parts: Optional[int] = None,
99
+ flags: Optional[Flags] = None,
100
+ lends: Optional[bool] = None,
101
+ ) -> None:
102
+ cluster = _jvm.jclass(f"{_CLUSTER}.ThreadedCluster")
103
+ # the constructor takes (threads, parts, flags, textual, lends): textual is for the tests of the JVM alone
104
+ arguments = _arguments(
105
+ threads,
106
+ _parts(parts),
107
+ flags and flags.jvm(),
108
+ None,
109
+ lends,
110
+ defaults=(cluster.getProcessors(), None, Flags.defaults().jvm(), False, None),
111
+ )
112
+ self._cluster = call(lambda: cluster(*arguments))
113
+
114
+ @property
115
+ def id(self) -> str:
116
+ """The identifier of the site of the cluster, as the others know it."""
117
+ return str(self._cluster.getId())
118
+
119
+ @property
120
+ def peers(self) -> set[str]:
121
+ """The sites the cluster is federated with."""
122
+ return {str(peer) for peer in self._cluster.getPeers()}
123
+
124
+ def listen(self, port: int = 0, host: str = "0.0.0.0") -> int:
125
+ """Accepts the links of other sites over WebSockets, on [port] of [host] (an ephemeral port for 0): the port."""
126
+ return int(call(lambda: self._cluster.listen(port, host)))
127
+
128
+ def discover(self) -> None:
129
+ """Federates the cluster with the clusters of the local network that discover the others as well."""
130
+ call(self._cluster.discover)
131
+
132
+ def connect(self, uri: str) -> None:
133
+ """Federates the cluster with the site listening at [uri] (ws://host:port)."""
134
+ call(lambda: self._cluster.connect(uri))
135
+
136
+ def load(self, theory: str) -> None:
137
+ """Replaces the theory with [theory], as Prolog text."""
138
+ call(lambda: self._cluster.load(theory))
139
+
140
+ def add(self, clause: str) -> None:
141
+ """Adds [clause], as Prolog text, to the theory."""
142
+ call(lambda: self._cluster.add(clause))
143
+
144
+ def reset(self) -> None:
145
+ """Forgets the theory."""
146
+ call(self._cluster.reset)
147
+
148
+ def solve(self, statement: str) -> EvaluationResponse:
149
+ """The arguments for [statement], as Prolog text, by label (Arg2pError for one that is not a statement)."""
150
+ return call(lambda: _response(self._cluster.solve(statement)))
151
+
152
+ def close(self) -> None:
153
+ """Closes the links to the other sites, and stops the threads."""
154
+ call(self._cluster.close)
155
+
156
+ def __enter__(self) -> ThreadedCluster:
157
+ return self
158
+
159
+ def __exit__(self, *_: Any) -> None:
160
+ self.close()
arg2p/py.typed ADDED
File without changes
arg2p/values.py ADDED
@@ -0,0 +1,59 @@
1
+ """The values arg2p gives and takes, as Python values: atoms as strings, numbers as numbers, lists as lists, compound
2
+ terms as [Struct] and variables as [Var] (the Value of the library of the JVM)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from dataclasses import dataclass, field
7
+ from typing import Any, Union
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class Var:
12
+ """A variable, by its name."""
13
+
14
+ name: str
15
+
16
+ def __str__(self) -> str:
17
+ return self.name
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class Struct:
22
+ """A compound term: its functor and arguments, and its text, as arg2p writes it (-a, o(-b))."""
23
+
24
+ functor: str
25
+ args: tuple[Any, ...]
26
+ text: str = field(default="", compare=False, repr=False)
27
+
28
+ @property
29
+ def arity(self) -> int:
30
+ return len(self.args)
31
+
32
+ def __str__(self) -> str:
33
+ return self.text or f"{self.functor}({', '.join(map(str, self.args))})"
34
+
35
+
36
+ Value = Union[str, int, float, list, Struct, Var]
37
+
38
+
39
+ def from_jvm(value: Any) -> Value:
40
+ """The Python value of [value], a Value of the library of the JVM."""
41
+ kind = str(value.getKind())
42
+ if kind == "atom":
43
+ return str(value.getName())
44
+ if kind == "integer":
45
+ return int(value.getValue())
46
+ if kind == "real":
47
+ return float(value.getValue())
48
+ if kind == "variable":
49
+ return Var(str(value.getName()))
50
+ if kind == "list":
51
+ return [from_jvm(item) for item in value.getItems()]
52
+ return Struct(str(value.getFunctor()), tuple(from_jvm(argument) for argument in value.getArgs()), str(value.toString()))
53
+
54
+
55
+ def text(value: Value) -> str:
56
+ """[value] as Prolog text, as arg2p writes it."""
57
+ if isinstance(value, list):
58
+ return "[" + ", ".join(text(item) for item in value) + "]"
59
+ return str(value)
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.4
2
+ Name: arg2p
3
+ Version: 0.19.0
4
+ Summary: Arg2P, structured argumentation in Prolog (ASPIC+, ABA, abstract frameworks), from Python
5
+ License: MIT
6
+ Project-URL: Homepage, https://tuprolog.github.io/arg2p-kt/
7
+ Project-URL: Source, https://github.com/tuProlog/arg2p-kt
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Java
11
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: JPype1>=1.5
15
+ Provides-Extra: test
16
+ Requires-Dist: pytest>=7; extra == "test"
17
+
18
+ # arg2p for Python
19
+
20
+ [Arg2P](https://github.com/tuProlog/arg2p-kt), structured argumentation in Prolog (ASPIC+, ABA, ABA+, abstract
21
+ frameworks, ...), from Python: the API of the library of the JVM, in snake case, run through
22
+ [JPype](https://jpype.readthedocs.io) on the Java runtime the wheel bundles.
23
+
24
+ ```sh
25
+ pip install arg2p
26
+ ```
27
+
28
+ ```python
29
+ from arg2p import Arg2p, Flags, Semantics
30
+
31
+ arg2p = Arg2p.of("""
32
+ r1 : [] => a.
33
+ r2 : [] => -a.
34
+ sup(r1, r2).
35
+ """)
36
+
37
+ arg2p.answer("a").in_statements # ['a']
38
+ (graph,) = arg2p.graphs()
39
+ graph.conclusions("out") # ['-a']
40
+
41
+ preferred = Arg2p.of("f1 :=> d.\nf2 :=> -d.", Flags.defaults().argument_labelling_mode(Semantics.PREFERRED))
42
+ len(preferred.graphs()) # 2
43
+
44
+ Arg2p.of("r1 :=> a.\nr2 : a => b.").causality.but_for("a", "b") # True
45
+ ```
46
+
47
+ See [the documentation](https://tuprolog.github.io/arg2p-kt/docs/api/python/) for the whole API: flags, graphs,
48
+ Prolog goals, causality, the parallel solver and the command line (`arg2p theory.pl -q a`).
49
+
50
+ ## Building
51
+
52
+ The package is a project of the Gradle build of Arg2P, which gives it its jars, its version and its Java runtime:
53
+
54
+ ```sh
55
+ ./gradlew :python:pythonJars :python:pythonVersion :python:pythonRuntime
56
+ pip install "./python[test]"
57
+ pytest python/tests
58
+ ```
59
+
60
+ Without the runtime (`pythonRuntime`), the package runs on the Java of the system (21 or later). `ARG2P_JVM` names
61
+ another library of the JVM, `ARG2P_CLASSPATH` other jars.
@@ -0,0 +1,41 @@
1
+ arg2p/__init__.py,sha256=VaOaJ19awRuY93DPAzCB5JqaGHFlVAHfCSC8MdAtYZU,1485
2
+ arg2p/__main__.py,sha256=IBERPBnGIm-t3j0CBQz8lCELY2uwkwnELKqnUF7P5Vw,2039
3
+ arg2p/_jvm.py,sha256=lF3GhYYcTtHXmpmnocH_nQAPpo0o2kBlSK5tt_FS9nQ,4670
4
+ arg2p/_version.py,sha256=X03rfGaW8B6UV-zl1XVzVWq_UtJ6lvQ5M9Cu6gVIUiE,104
5
+ arg2p/arg2p.py,sha256=rByUwohd4mWRGcGLtm8ZTtyM7f2dKTXDxTA8y5LN9jk,10814
6
+ arg2p/flags.py,sha256=qgTa2N7FopZYivMz3EUth986e35gye52c7xDSITDc3I,7681
7
+ arg2p/model.py,sha256=RUtyw0N6bWeRo6Mrb1EXuzqPdr--CFAwUPPPoccFu6A,3626
8
+ arg2p/parallel.py,sha256=HUGEny_z2WYqgrYBJunNLttme4Yc4DrYVHt8VPENGWk,6511
9
+ arg2p/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ arg2p/values.py,sha256=CQTeZzlOFbp2Usvh6d8H4bFJ-CNzW0SD144th8BgId4,1745
11
+ arg2p/jars/Java-WebSocket-1.6.0.jar,sha256=6uKSE-TxZRVjnCiVcgDwEbOWf__K2hlizwJV0kkZwi8,140686
12
+ arg2p/jars/annotations-13.0.jar,sha256=rOKhDcji1f00kl7KwD5JiLLA-FFlDJS4zvSbob0RFHg,17536
13
+ arg2p/jars/arg2p-jvm-0.19.0.jar,sha256=Nq935txV2kDy38S82YZxvGtSdm9wgr8sVlGiXowATvI,23984
14
+ arg2p/jars/causality-solver-jvm-0.19.0.jar,sha256=gq2xesJatdYI4_uL2a3e_WV-DE8p58n7tqajHAV-QdQ,29646
15
+ arg2p/jars/core-jvm-0.19.0.jar,sha256=9t21VrkMpi9su5E9vIKsAhwCTDmEB4m8eBlwgQhI3DY,803054
16
+ arg2p/jars/core-jvm-3.0.5.jar,sha256=W8JsG1g2admKcubqpIFESmtofvuhem_d1SbfHzZYxIo,514768
17
+ arg2p/jars/dsl-core-jvm-3.0.5.jar,sha256=bLpuEIka4AcqIMrYTWsYi52VYMOnxXAGu_Jmcno3ibM,95802
18
+ arg2p/jars/dsl-solve-jvm-3.0.5.jar,sha256=7tuvf0XC0FQcLhZgZPmT4WNyATDPxOLb-iV6ZVsxBbc,44014
19
+ arg2p/jars/dsl-theory-jvm-3.0.5.jar,sha256=SyDumXmzuv-YvDx3jEaRbfmX5JfeQ41cO2j7jtNh7HA,38502
20
+ arg2p/jars/dsl-unify-jvm-3.0.5.jar,sha256=aABWIQEEoMF7Vs31kKN6hBNlKjx0M4WTVjpv-1QoGKI,35118
21
+ arg2p/jars/io-lib-jvm-3.0.5.jar,sha256=2ICtLyI27Y0AtdGpQELS4NgQmRecZ_NN-R28fkpsWkY,163252
22
+ arg2p/jars/kotlin-reflect-2.4.21.jar,sha256=fJ6L2jyvqFtlaSBlrNIleLKEMpK05LqoNhvckXV0bog,3808541
23
+ arg2p/jars/kotlin-stdlib-2.4.21.jar,sha256=vYJQIQWEy2WYR9zpzaZg-OmQbl3vf7686pPcbctqiKM,1853315
24
+ arg2p/jars/kotlin-stdlib-jdk7-2.4.21.jar,sha256=b86GfUwkySqLZMlmMX06wdS3h6AOGqbSt2sc7dOgwII,940
25
+ arg2p/jars/kotlin-stdlib-jdk8-2.4.21.jar,sha256=64PLwH7mj81fIpsu0dZsegF1xWrB3KFFfM5LFwIAQlk,946
26
+ arg2p/jars/kt-math-jvm-0.10.2.jar,sha256=0nRGvmUidmbv5DDVXMbvNkcHWbSVIXiiMysNcKu3JBc,179877
27
+ arg2p/jars/okio-jvm-3.18.2.jar,sha256=aB_tVJ0dLGNxD0LJ6rQz1kLHGvd4ur1nA1xQ-MQlqv8,392462
28
+ arg2p/jars/parallel-solver-jvm-0.19.0.jar,sha256=mFkWvFV-Ab_DFRFzrGzxKZ6urqK50p4GhA7OzT4_Nws,199455
29
+ arg2p/jars/parser-core-jvm-3.0.5.jar,sha256=sAvBRMjPBxMHUAYAmpiU7VAA9Vkeu1r6bkxYW-Qll9E,38199
30
+ arg2p/jars/parser-impl-jvm-3.0.5.jar,sha256=KXlQDl-hoaG0sRSjK8zz4DGXnBFsLr5OdaFAe6wJCw8,279480
31
+ arg2p/jars/parser-theory-jvm-3.0.5.jar,sha256=qL_uRjKMUMtZcFtCCLJsL7NWSXr5-UYOQEckEHW2pUI,22496
32
+ arg2p/jars/slf4j-api-2.0.13.jar,sha256=58KkjoUVuh9J-mN9V7Ti9ZCz9b2XQHrGmcOqXvsSBKk,68605
33
+ arg2p/jars/solve-jvm-3.0.5.jar,sha256=hkpXrzHMGM1I-zPv_ZsMmpR-8ygSgS7hsSSX2-M6w54,931229
34
+ arg2p/jars/theory-jvm-3.0.5.jar,sha256=4UhU54ELaaheyQorPYFY5_e_52NZeTjbeLoGT2Gu_Pk,265790
35
+ arg2p/jars/unify-jvm-3.0.5.jar,sha256=_3qdo05PayxsoZFPvrKeUeCIoZZUSVDWBaCP0z3ihiw,60745
36
+ arg2p/jars/utils-jvm-3.0.5.jar,sha256=Cbg4lCn-XC8-ekinha_JdywjEJ7cq69cz9PE4DQFiac,156120
37
+ arg2p-0.19.0.dist-info/METADATA,sha256=gqDR-DKVdiYRlJC2jqHrLhrFm4W650D233thSFXzGnM,2117
38
+ arg2p-0.19.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
39
+ arg2p-0.19.0.dist-info/entry_points.txt,sha256=xKVvdOCOFphBOAu3DDC2C8z0S46Twj23c78iHGDIpwM,46
40
+ arg2p-0.19.0.dist-info/top_level.txt,sha256=m2bPCzU2ZXpzuE8dHtKOP9xjcisi2mFm_sDKVgOzzE4,6
41
+ arg2p-0.19.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ arg2p = arg2p.__main__:main
@@ -0,0 +1 @@
1
+ arg2p