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.
- arg2p/__init__.py +63 -0
- arg2p/__main__.py +49 -0
- arg2p/_jvm.py +145 -0
- arg2p/_version.py +2 -0
- arg2p/arg2p.py +271 -0
- arg2p/flags.py +224 -0
- arg2p/jars/Java-WebSocket-1.6.0.jar +0 -0
- arg2p/jars/annotations-13.0.jar +0 -0
- arg2p/jars/arg2p-jvm-0.19.0.jar +0 -0
- arg2p/jars/causality-solver-jvm-0.19.0.jar +0 -0
- arg2p/jars/core-jvm-0.19.0.jar +0 -0
- arg2p/jars/core-jvm-3.0.5.jar +0 -0
- arg2p/jars/dsl-core-jvm-3.0.5.jar +0 -0
- arg2p/jars/dsl-solve-jvm-3.0.5.jar +0 -0
- arg2p/jars/dsl-theory-jvm-3.0.5.jar +0 -0
- arg2p/jars/dsl-unify-jvm-3.0.5.jar +0 -0
- arg2p/jars/io-lib-jvm-3.0.5.jar +0 -0
- arg2p/jars/kotlin-reflect-2.4.21.jar +0 -0
- arg2p/jars/kotlin-stdlib-2.4.21.jar +0 -0
- arg2p/jars/kotlin-stdlib-jdk7-2.4.21.jar +0 -0
- arg2p/jars/kotlin-stdlib-jdk8-2.4.21.jar +0 -0
- arg2p/jars/kt-math-jvm-0.10.2.jar +0 -0
- arg2p/jars/okio-jvm-3.18.2.jar +0 -0
- arg2p/jars/parallel-solver-jvm-0.19.0.jar +0 -0
- arg2p/jars/parser-core-jvm-3.0.5.jar +0 -0
- arg2p/jars/parser-impl-jvm-3.0.5.jar +0 -0
- arg2p/jars/parser-theory-jvm-3.0.5.jar +0 -0
- arg2p/jars/slf4j-api-2.0.13.jar +0 -0
- arg2p/jars/solve-jvm-3.0.5.jar +0 -0
- arg2p/jars/theory-jvm-3.0.5.jar +0 -0
- arg2p/jars/unify-jvm-3.0.5.jar +0 -0
- arg2p/jars/utils-jvm-3.0.5.jar +0 -0
- arg2p/model.py +102 -0
- arg2p/parallel.py +160 -0
- arg2p/py.typed +0 -0
- arg2p/values.py +59 -0
- arg2p-0.19.0.dist-info/METADATA +61 -0
- arg2p-0.19.0.dist-info/RECORD +41 -0
- arg2p-0.19.0.dist-info/WHEEL +5 -0
- arg2p-0.19.0.dist-info/entry_points.txt +2 -0
- 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
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
|
|
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 @@
|
|
|
1
|
+
arg2p
|