clingexplaid 1.2.2__tar.gz → 1.3.0__tar.gz
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.
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/PKG-INFO +37 -18
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/README.md +36 -17
- clingexplaid-1.3.0/src/clingexplaid/cli/style/mus.py +5 -0
- clingexplaid-1.3.0/src/clingexplaid/exceptions/__init__.py +8 -0
- clingexplaid-1.3.0/src/clingexplaid/preprocessors/__init__.py +5 -0
- clingexplaid-1.3.0/src/clingexplaid/preprocessors/preprocessor_assumption.py +210 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/transformers/__init__.py +0 -2
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid.egg-info/PKG-INFO +37 -18
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid.egg-info/SOURCES.txt +6 -2
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/transformed_program_assumptions_all.lp +1 -1
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/transformed_program_assumptions_certain_signatures.lp +1 -1
- clingexplaid-1.3.0/tests/clingexplaid/res/transformed_program_nothing_transformed.lp +13 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/test_muc.py +13 -14
- clingexplaid-1.3.0/tests/clingexplaid/test_preprocessors.py +88 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/test_transformers.py +0 -69
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/test_utils.py +27 -0
- clingexplaid-1.2.2/src/clingexplaid/transformers/exceptions.py +0 -15
- clingexplaid-1.2.2/src/clingexplaid/transformers/transformer_assumption.py +0 -151
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/.envrc +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/.github/workflows/deploy.yml +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/.github/workflows/test.yml +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/.gitignore +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/.pre-commit-config.yaml +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/.readthedocs.yaml +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/CHANGES.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/CONTRIBUTING.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/DEPLOYMENT.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/DEVELOPMENT.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/LICENSE +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/LIMITATIONS.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/TODO.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/doc/Makefile +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/doc/_static/css/custom.css +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/doc/_static/logo-dark-mode.png +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/doc/_static/logo-light-mode.png +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/doc/conf.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/doc/content/encodings/index.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/doc/content/encodings/instance.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/doc/content/installation.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/doc/content/quickstart.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/doc/index.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/graph_coloring/README.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/graph_coloring/encoding.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/graph_coloring/graph_coloring_example.svg +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/graph_coloring/instance.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/bad_mucs.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/sat.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/sat_simple.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/simple.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/soup.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/sudoku_4x4.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/sudoku_encoding.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/sudoku_encoding_2.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/test/blob.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/test.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/test2.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/test3.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/test4.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/test_inert.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/x.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/misc/zero_arity_assumptions.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/queens/README.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/queens/encoding.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/queens/instance.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/queens/queens_example.svg +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/sudoku/README.md +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/sudoku/encoding.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/sudoku/instance.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/examples/sudoku/sudoku_example.svg +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/noxfile.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/pyproject.toml +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/setup.cfg +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/__init__.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/mus/__init__.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/mus/core_computer.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/propagators/__init__.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/propagators/constants.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/propagators/propagator_solver_decisions.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/py.typed +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/transformers/constants.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/transformers/transformer_constraint.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/transformers/transformer_fact.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/transformers/transformer_optimization_remover.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/transformers/transformer_rule_id.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/transformers/transformer_rule_splitter.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/unsat_constraints/__init__.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/unsat_constraints/constants.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/unsat_constraints/unsat_constraint_computer.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/utils/__init__.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/utils/logging.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/utils/match.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/utils/parser.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/utils/symbols.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid/utils/types.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid.egg-info/dependency_links.txt +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid.egg-info/requires.txt +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/src/clingexplaid.egg-info/top_level.txt +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/__init__.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/__init__.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/test_program.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/test_program_constants.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/test_program_constraints.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/test_program_decision_order.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/test_program_multi_mus.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/test_program_optimization.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/test_program_pattern.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/test_program_rules.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/test_program_unsat_constraints.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/transformed_program_constraints.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/transformed_program_constraints_id.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/transformed_program_facts.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/transformed_program_optimization.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/transformed_program_pattern.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/transformed_program_rule_ids.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/res/transformed_program_rules_split.lp +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/test_main.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/clingexplaid/test_unsat_constraints.py +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/py.typed +0 -0
- {clingexplaid-1.2.2 → clingexplaid-1.3.0}/tests/test_main.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clingexplaid
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Tools to aid the development of explanation systems using clingo
|
|
5
5
|
Author-email: Hannes Weichelt <hweichelt@uni-potsdam.de>, Susana Hahn <susuhahnml@yahoo.com.mx>
|
|
6
6
|
Maintainer-email: Hannes Weichelt <hweichelt@uni-potsdam.de>
|
|
@@ -96,12 +96,37 @@ Here are two example for using `clingexplaid`'s API.
|
|
|
96
96
|
|
|
97
97
|
### Minimal Unsatisfiable Subsets (MUS)
|
|
98
98
|
|
|
99
|
+
Transforming facts to Assumptions (necessary pre-processing step):
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from clingexplaid.preprocessors import AssumptionPreprocessor
|
|
103
|
+
from clingexplaid.preprocessors import (
|
|
104
|
+
FilterSignature,
|
|
105
|
+
FilterPattern,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
PROGRAM = """
|
|
109
|
+
a(book;magazine;video).
|
|
110
|
+
b(test).
|
|
111
|
+
c(1..10).
|
|
112
|
+
d(1..3).
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
ap = AssumptionPreprocessor(filters=[
|
|
116
|
+
FilterSignature("a", 1),
|
|
117
|
+
FilterPattern("d(2)")
|
|
118
|
+
])
|
|
119
|
+
result = ap.process(PROGRAM)
|
|
120
|
+
# You can either use the return value of `ap.process`
|
|
121
|
+
print(result)
|
|
122
|
+
# Or use `ap.control` with your transformed program already added
|
|
123
|
+
print(ap.control)
|
|
124
|
+
```
|
|
125
|
+
|
|
99
126
|
Getting a single MUS:
|
|
100
127
|
|
|
101
128
|
```python
|
|
102
|
-
import
|
|
103
|
-
from clingexplaid.transformers import AssumptionTransformer
|
|
104
|
-
from clingexplaid.transformers.transformer_assumption import FilterSignature
|
|
129
|
+
from clingexplaid.preprocessors import AssumptionPreprocessor, FilterSignature
|
|
105
130
|
from clingexplaid.mus import CoreComputer
|
|
106
131
|
|
|
107
132
|
PROGRAM = """
|
|
@@ -113,25 +138,19 @@ a(X) :- b(X).
|
|
|
113
138
|
:- a(X), X>=3.
|
|
114
139
|
"""
|
|
115
140
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
control.add("base", [], transformed_program)
|
|
122
|
-
control.ground([("base", [])])
|
|
123
|
-
|
|
124
|
-
assumptions = at.get_assumption_literals(control)
|
|
125
|
-
|
|
126
|
-
cc = CoreComputer(control, assumptions)
|
|
127
|
-
|
|
141
|
+
ap = AssumptionPreprocessor(filters={FilterSignature("a", 1)})
|
|
142
|
+
ap.process(PROGRAM)
|
|
143
|
+
ap.control.ground([("base", [])])
|
|
144
|
+
cc = CoreComputer(ap.control, ap.assumptions)
|
|
128
145
|
|
|
129
146
|
def shrink_on_core(core) -> None:
|
|
130
147
|
mus_literals = cc.shrink(core)
|
|
131
148
|
print("MUS:", cc.mus_to_string(mus_literals))
|
|
132
149
|
|
|
133
|
-
|
|
134
|
-
|
|
150
|
+
ap.control.solve(
|
|
151
|
+
assumptions=list(ap.assumptions),
|
|
152
|
+
on_core=shrink_on_core
|
|
153
|
+
)
|
|
135
154
|
```
|
|
136
155
|
|
|
137
156
|
Getting multiple MUS:
|
|
@@ -27,12 +27,37 @@ Here are two example for using `clingexplaid`'s API.
|
|
|
27
27
|
|
|
28
28
|
### Minimal Unsatisfiable Subsets (MUS)
|
|
29
29
|
|
|
30
|
+
Transforming facts to Assumptions (necessary pre-processing step):
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from clingexplaid.preprocessors import AssumptionPreprocessor
|
|
34
|
+
from clingexplaid.preprocessors import (
|
|
35
|
+
FilterSignature,
|
|
36
|
+
FilterPattern,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
PROGRAM = """
|
|
40
|
+
a(book;magazine;video).
|
|
41
|
+
b(test).
|
|
42
|
+
c(1..10).
|
|
43
|
+
d(1..3).
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
ap = AssumptionPreprocessor(filters=[
|
|
47
|
+
FilterSignature("a", 1),
|
|
48
|
+
FilterPattern("d(2)")
|
|
49
|
+
])
|
|
50
|
+
result = ap.process(PROGRAM)
|
|
51
|
+
# You can either use the return value of `ap.process`
|
|
52
|
+
print(result)
|
|
53
|
+
# Or use `ap.control` with your transformed program already added
|
|
54
|
+
print(ap.control)
|
|
55
|
+
```
|
|
56
|
+
|
|
30
57
|
Getting a single MUS:
|
|
31
58
|
|
|
32
59
|
```python
|
|
33
|
-
import
|
|
34
|
-
from clingexplaid.transformers import AssumptionTransformer
|
|
35
|
-
from clingexplaid.transformers.transformer_assumption import FilterSignature
|
|
60
|
+
from clingexplaid.preprocessors import AssumptionPreprocessor, FilterSignature
|
|
36
61
|
from clingexplaid.mus import CoreComputer
|
|
37
62
|
|
|
38
63
|
PROGRAM = """
|
|
@@ -44,25 +69,19 @@ a(X) :- b(X).
|
|
|
44
69
|
:- a(X), X>=3.
|
|
45
70
|
"""
|
|
46
71
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
control.add("base", [], transformed_program)
|
|
53
|
-
control.ground([("base", [])])
|
|
54
|
-
|
|
55
|
-
assumptions = at.get_assumption_literals(control)
|
|
56
|
-
|
|
57
|
-
cc = CoreComputer(control, assumptions)
|
|
58
|
-
|
|
72
|
+
ap = AssumptionPreprocessor(filters={FilterSignature("a", 1)})
|
|
73
|
+
ap.process(PROGRAM)
|
|
74
|
+
ap.control.ground([("base", [])])
|
|
75
|
+
cc = CoreComputer(ap.control, ap.assumptions)
|
|
59
76
|
|
|
60
77
|
def shrink_on_core(core) -> None:
|
|
61
78
|
mus_literals = cc.shrink(core)
|
|
62
79
|
print("MUS:", cc.mus_to_string(mus_literals))
|
|
63
80
|
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
ap.control.solve(
|
|
82
|
+
assumptions=list(ap.assumptions),
|
|
83
|
+
on_core=shrink_on_core
|
|
84
|
+
)
|
|
66
85
|
```
|
|
67
86
|
|
|
68
87
|
Getting multiple MUS:
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Transformer Module: Assumption Transformer for converting facts to choices that can be assumed
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Dict, Iterable, List, Optional, Set, Tuple, Union
|
|
8
|
+
|
|
9
|
+
import clingo
|
|
10
|
+
from clingo.ast import ProgramBuilder, parse_string
|
|
11
|
+
|
|
12
|
+
from ..exceptions import UnprocessedException
|
|
13
|
+
from ..utils.match import match
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class FilterSignature:
|
|
18
|
+
"""Filters the facts converted to assumptions by signature"""
|
|
19
|
+
|
|
20
|
+
name: str
|
|
21
|
+
arity: int
|
|
22
|
+
|
|
23
|
+
def __hash__(self) -> int:
|
|
24
|
+
return hash((self.name, self.arity))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class FilterPattern:
|
|
29
|
+
"""Filters the facts converted to assumptions by pattern"""
|
|
30
|
+
|
|
31
|
+
pattern: str
|
|
32
|
+
|
|
33
|
+
def __hash__(self) -> int:
|
|
34
|
+
return hash(self.pattern)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AssumptionPreprocessor:
|
|
38
|
+
"""
|
|
39
|
+
A transformer that transforms facts that match with one of the signatures provided (no signatures means all facts)
|
|
40
|
+
into choice rules and also provides the according assumptions for them.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
# pylint: disable=too-many-instance-attributes
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
filters: Optional[Iterable[Union[FilterPattern, FilterSignature]]] = None,
|
|
48
|
+
control: Optional[clingo.Control] = None,
|
|
49
|
+
fail_on_unprocessed: bool = True,
|
|
50
|
+
):
|
|
51
|
+
self.control = control if control is not None else clingo.Control()
|
|
52
|
+
self.filters: Set[Union[FilterPattern, FilterSignature]] = set(filters) if filters is not None else set()
|
|
53
|
+
self._filters_convert_nothing = bool(filters is not None and len(list(filters)) == 0)
|
|
54
|
+
self._processed = False
|
|
55
|
+
self._fail_on_unprocessed = fail_on_unprocessed
|
|
56
|
+
self._parsed_rules: List[str] = []
|
|
57
|
+
self._constants: Dict[str, clingo.Symbol] = {}
|
|
58
|
+
self._assumptions: Set[Tuple[clingo.Symbol, bool]] = set()
|
|
59
|
+
|
|
60
|
+
if self._filters_convert_nothing:
|
|
61
|
+
warnings.warn("When an empty list of filters is provided, no facts will be transformed to assumptions")
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def _to_ast(symbol: Union[str, clingo.Symbol]) -> clingo.ast.AST:
|
|
65
|
+
parsed_ast: List[clingo.ast.AST] = []
|
|
66
|
+
parse_string(f"{symbol}.", parsed_ast.append)
|
|
67
|
+
ast_symbol = parsed_ast[1] # return AST symbol (parsed_ast[0] = '#program base.')
|
|
68
|
+
return ast_symbol
|
|
69
|
+
|
|
70
|
+
def _add_assumption(self, symbol: clingo.Symbol, positive: bool) -> None:
|
|
71
|
+
self._assumptions.add((symbol, positive))
|
|
72
|
+
|
|
73
|
+
def _add_assumption_string(self, string: str, positive: bool) -> None:
|
|
74
|
+
self._add_assumption(clingo.parse_term(string), positive)
|
|
75
|
+
|
|
76
|
+
def _add_constant(self, name: str, symbol: clingo.Symbol) -> None:
|
|
77
|
+
self._constants[name] = symbol
|
|
78
|
+
|
|
79
|
+
def _any_filters_apply(self, symbol: clingo.Symbol) -> bool:
|
|
80
|
+
if self._filters_convert_nothing:
|
|
81
|
+
# Convert nothing
|
|
82
|
+
return False
|
|
83
|
+
if not self._filters_convert_nothing and len(self.filters) == 0:
|
|
84
|
+
# Convert everything
|
|
85
|
+
return True
|
|
86
|
+
applies = False
|
|
87
|
+
for symbol_filter in self.filters:
|
|
88
|
+
match symbol_filter:
|
|
89
|
+
case FilterPattern(pattern=pattern):
|
|
90
|
+
applies |= bool(match(pattern, symbol))
|
|
91
|
+
case FilterSignature(name=name, arity=arity):
|
|
92
|
+
applies |= len(symbol.arguments) == arity and symbol.name == name
|
|
93
|
+
return applies
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def _unpool(ast_symbol: clingo.ast.ASTType.SymbolicAtom) -> Set[clingo.Symbol]:
|
|
97
|
+
if ".." in str(ast_symbol):
|
|
98
|
+
# Case range in ast symbol (i.e. 1..10)
|
|
99
|
+
# Solved using grounding, but if possible, I'd rather avoid this if possible
|
|
100
|
+
unpool_control = clingo.Control()
|
|
101
|
+
unpool_control.add(f"{ast_symbol}.")
|
|
102
|
+
unpool_control.ground([("base", [])])
|
|
103
|
+
with unpool_control.solve(yield_=True) as solve_handle:
|
|
104
|
+
result = solve_handle.get()
|
|
105
|
+
if result.satisfiable:
|
|
106
|
+
model = solve_handle.model()
|
|
107
|
+
return set(model.symbols(atoms=True))
|
|
108
|
+
raise ValueError("Provided AST symbol does not follow valid ASP syntax") # nocoverage
|
|
109
|
+
# Case default
|
|
110
|
+
atoms_unpooled = ast_symbol.unpool()
|
|
111
|
+
return {clingo.parse_term(str(a)) for a in atoms_unpooled}
|
|
112
|
+
|
|
113
|
+
def _transform_rule(self, rule: clingo.ast.ASTType.Rule) -> List[clingo.ast.ASTType.Rule]:
|
|
114
|
+
if rule.head.ast_type != clingo.ast.ASTType.Literal:
|
|
115
|
+
return [rule]
|
|
116
|
+
if rule.body:
|
|
117
|
+
return [rule]
|
|
118
|
+
|
|
119
|
+
atoms_unpooled = AssumptionPreprocessor._unpool(rule.head)
|
|
120
|
+
atoms_choice: Set[clingo.ast.AST] = set()
|
|
121
|
+
atoms_retained: Set[clingo.Symbol] = set()
|
|
122
|
+
for atom in atoms_unpooled:
|
|
123
|
+
filters_apply = self._any_filters_apply(atom)
|
|
124
|
+
# if filters are defined, only transform facts that match them, else transform all facts
|
|
125
|
+
if not filters_apply:
|
|
126
|
+
atoms_retained.add(atom)
|
|
127
|
+
continue
|
|
128
|
+
ast = AssumptionPreprocessor._to_ast(atom)
|
|
129
|
+
ast_choice_literal = clingo.ast.ConditionalLiteral(location=rule.location, literal=ast.head, condition=[])
|
|
130
|
+
atoms_choice.add(ast_choice_literal)
|
|
131
|
+
atoms_retained_ast: Set[clingo.ast.AST] = set()
|
|
132
|
+
for atom in atoms_retained:
|
|
133
|
+
atoms_retained_ast.add(AssumptionPreprocessor._to_ast(atom))
|
|
134
|
+
|
|
135
|
+
if len(atoms_choice) > 0:
|
|
136
|
+
# Add all choice atoms to the assumption list
|
|
137
|
+
for a_choice in atoms_choice:
|
|
138
|
+
self._add_assumption_string(str(a_choice), True)
|
|
139
|
+
|
|
140
|
+
choice_rule = clingo.ast.Rule(
|
|
141
|
+
location=rule.location,
|
|
142
|
+
head=clingo.ast.Aggregate(
|
|
143
|
+
location=rule.location,
|
|
144
|
+
left_guard=None,
|
|
145
|
+
elements=list(sorted(atoms_choice, key=str)),
|
|
146
|
+
right_guard=None,
|
|
147
|
+
),
|
|
148
|
+
body=[],
|
|
149
|
+
)
|
|
150
|
+
return [choice_rule, *sorted(atoms_retained_ast, key=str)]
|
|
151
|
+
return list(sorted(atoms_retained_ast, key=str))
|
|
152
|
+
|
|
153
|
+
def register_ast(self, ast: clingo.ast.AST, builder: clingo.ast.ProgramBuilder) -> None:
|
|
154
|
+
"""Registers the provided AST to the builder and the parsed rules list"""
|
|
155
|
+
if ast.ast_type == clingo.ast.ASTType.Definition:
|
|
156
|
+
self._add_constant(str(ast.name), ast.value.symbol)
|
|
157
|
+
self._parsed_rules.append(str(ast))
|
|
158
|
+
warnings.warn(
|
|
159
|
+
"Currently the ProgramBuilder does not fill the control. Please add the processed programm manually."
|
|
160
|
+
)
|
|
161
|
+
builder.add(ast)
|
|
162
|
+
|
|
163
|
+
def process(self, program_string: str) -> str:
|
|
164
|
+
"""Processes the provided program string and returns the transformed program string (control is also updated)"""
|
|
165
|
+
control = clingo.Control("0")
|
|
166
|
+
ast_list: List[clingo.ast.AST] = []
|
|
167
|
+
with ProgramBuilder(control) as builder:
|
|
168
|
+
parse_string(program_string, ast_list.append)
|
|
169
|
+
for ast in ast_list:
|
|
170
|
+
if ast.ast_type == clingo.ast.ASTType.Rule:
|
|
171
|
+
for new_ast in self._transform_rule(ast):
|
|
172
|
+
if new_ast.ast_type != clingo.ast.ASTType.Rule: # nocoverage
|
|
173
|
+
new_rule = clingo.ast.Rule(location=ast.location, head=new_ast, body=[])
|
|
174
|
+
self.register_ast(new_rule, builder)
|
|
175
|
+
else:
|
|
176
|
+
self.register_ast(new_ast, builder)
|
|
177
|
+
elif ast.ast_type == clingo.ast.ASTType.Definition:
|
|
178
|
+
self.register_ast(ast, builder)
|
|
179
|
+
else:
|
|
180
|
+
self.register_ast(ast, builder)
|
|
181
|
+
self._processed = True
|
|
182
|
+
return "\n".join(self._parsed_rules)
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def assumptions(self) -> Set[Tuple[clingo.Symbol, bool]]:
|
|
186
|
+
"""Property that returns the assumptions generated by the preprocessor after calling `process`"""
|
|
187
|
+
if not self._processed:
|
|
188
|
+
if self._fail_on_unprocessed:
|
|
189
|
+
raise UnprocessedException(
|
|
190
|
+
"It is impossible to retrieve assumptions without invoking the `process` function first"
|
|
191
|
+
)
|
|
192
|
+
warnings.warn(
|
|
193
|
+
"Unprocessed Error: It is impossible to retrieve assumptions without invoking the "
|
|
194
|
+
"`process` function first"
|
|
195
|
+
) # nocoverage
|
|
196
|
+
return set(self._assumptions)
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def constants(self) -> Dict[str, clingo.Symbol]:
|
|
200
|
+
"""Property that returns the constants generated by the preprocessor after calling `process`"""
|
|
201
|
+
if not self._processed: # nocoverage
|
|
202
|
+
if self._fail_on_unprocessed:
|
|
203
|
+
raise UnprocessedException(
|
|
204
|
+
"It is impossible to retrieve constants without invoking the `process` function first"
|
|
205
|
+
)
|
|
206
|
+
warnings.warn(
|
|
207
|
+
"Unprocessed Error: It is impossible to retrieve constants without invoking the "
|
|
208
|
+
"`process function first`"
|
|
209
|
+
)
|
|
210
|
+
return self._constants
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Transformers for Explanation
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from .transformer_assumption import AssumptionTransformer
|
|
6
5
|
from .transformer_constraint import ConstraintTransformer
|
|
7
6
|
from .transformer_fact import FactTransformer
|
|
8
7
|
from .transformer_optimization_remover import OptimizationRemover
|
|
@@ -10,7 +9,6 @@ from .transformer_rule_id import RuleIDTransformer
|
|
|
10
9
|
from .transformer_rule_splitter import RuleSplitter
|
|
11
10
|
|
|
12
11
|
__all__ = [
|
|
13
|
-
"AssumptionTransformer",
|
|
14
12
|
"ConstraintTransformer",
|
|
15
13
|
"FactTransformer",
|
|
16
14
|
"OptimizationRemover",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clingexplaid
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Tools to aid the development of explanation systems using clingo
|
|
5
5
|
Author-email: Hannes Weichelt <hweichelt@uni-potsdam.de>, Susana Hahn <susuhahnml@yahoo.com.mx>
|
|
6
6
|
Maintainer-email: Hannes Weichelt <hweichelt@uni-potsdam.de>
|
|
@@ -96,12 +96,37 @@ Here are two example for using `clingexplaid`'s API.
|
|
|
96
96
|
|
|
97
97
|
### Minimal Unsatisfiable Subsets (MUS)
|
|
98
98
|
|
|
99
|
+
Transforming facts to Assumptions (necessary pre-processing step):
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from clingexplaid.preprocessors import AssumptionPreprocessor
|
|
103
|
+
from clingexplaid.preprocessors import (
|
|
104
|
+
FilterSignature,
|
|
105
|
+
FilterPattern,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
PROGRAM = """
|
|
109
|
+
a(book;magazine;video).
|
|
110
|
+
b(test).
|
|
111
|
+
c(1..10).
|
|
112
|
+
d(1..3).
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
ap = AssumptionPreprocessor(filters=[
|
|
116
|
+
FilterSignature("a", 1),
|
|
117
|
+
FilterPattern("d(2)")
|
|
118
|
+
])
|
|
119
|
+
result = ap.process(PROGRAM)
|
|
120
|
+
# You can either use the return value of `ap.process`
|
|
121
|
+
print(result)
|
|
122
|
+
# Or use `ap.control` with your transformed program already added
|
|
123
|
+
print(ap.control)
|
|
124
|
+
```
|
|
125
|
+
|
|
99
126
|
Getting a single MUS:
|
|
100
127
|
|
|
101
128
|
```python
|
|
102
|
-
import
|
|
103
|
-
from clingexplaid.transformers import AssumptionTransformer
|
|
104
|
-
from clingexplaid.transformers.transformer_assumption import FilterSignature
|
|
129
|
+
from clingexplaid.preprocessors import AssumptionPreprocessor, FilterSignature
|
|
105
130
|
from clingexplaid.mus import CoreComputer
|
|
106
131
|
|
|
107
132
|
PROGRAM = """
|
|
@@ -113,25 +138,19 @@ a(X) :- b(X).
|
|
|
113
138
|
:- a(X), X>=3.
|
|
114
139
|
"""
|
|
115
140
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
control.add("base", [], transformed_program)
|
|
122
|
-
control.ground([("base", [])])
|
|
123
|
-
|
|
124
|
-
assumptions = at.get_assumption_literals(control)
|
|
125
|
-
|
|
126
|
-
cc = CoreComputer(control, assumptions)
|
|
127
|
-
|
|
141
|
+
ap = AssumptionPreprocessor(filters={FilterSignature("a", 1)})
|
|
142
|
+
ap.process(PROGRAM)
|
|
143
|
+
ap.control.ground([("base", [])])
|
|
144
|
+
cc = CoreComputer(ap.control, ap.assumptions)
|
|
128
145
|
|
|
129
146
|
def shrink_on_core(core) -> None:
|
|
130
147
|
mus_literals = cc.shrink(core)
|
|
131
148
|
print("MUS:", cc.mus_to_string(mus_literals))
|
|
132
149
|
|
|
133
|
-
|
|
134
|
-
|
|
150
|
+
ap.control.solve(
|
|
151
|
+
assumptions=list(ap.assumptions),
|
|
152
|
+
on_core=shrink_on_core
|
|
153
|
+
)
|
|
135
154
|
```
|
|
136
155
|
|
|
137
156
|
Getting multiple MUS:
|
|
@@ -59,15 +59,17 @@ src/clingexplaid.egg-info/SOURCES.txt
|
|
|
59
59
|
src/clingexplaid.egg-info/dependency_links.txt
|
|
60
60
|
src/clingexplaid.egg-info/requires.txt
|
|
61
61
|
src/clingexplaid.egg-info/top_level.txt
|
|
62
|
+
src/clingexplaid/cli/style/mus.py
|
|
63
|
+
src/clingexplaid/exceptions/__init__.py
|
|
62
64
|
src/clingexplaid/mus/__init__.py
|
|
63
65
|
src/clingexplaid/mus/core_computer.py
|
|
66
|
+
src/clingexplaid/preprocessors/__init__.py
|
|
67
|
+
src/clingexplaid/preprocessors/preprocessor_assumption.py
|
|
64
68
|
src/clingexplaid/propagators/__init__.py
|
|
65
69
|
src/clingexplaid/propagators/constants.py
|
|
66
70
|
src/clingexplaid/propagators/propagator_solver_decisions.py
|
|
67
71
|
src/clingexplaid/transformers/__init__.py
|
|
68
72
|
src/clingexplaid/transformers/constants.py
|
|
69
|
-
src/clingexplaid/transformers/exceptions.py
|
|
70
|
-
src/clingexplaid/transformers/transformer_assumption.py
|
|
71
73
|
src/clingexplaid/transformers/transformer_constraint.py
|
|
72
74
|
src/clingexplaid/transformers/transformer_fact.py
|
|
73
75
|
src/clingexplaid/transformers/transformer_optimization_remover.py
|
|
@@ -88,6 +90,7 @@ tests/test_main.py
|
|
|
88
90
|
tests/clingexplaid/__init__.py
|
|
89
91
|
tests/clingexplaid/test_main.py
|
|
90
92
|
tests/clingexplaid/test_muc.py
|
|
93
|
+
tests/clingexplaid/test_preprocessors.py
|
|
91
94
|
tests/clingexplaid/test_transformers.py
|
|
92
95
|
tests/clingexplaid/test_unsat_constraints.py
|
|
93
96
|
tests/clingexplaid/test_utils.py
|
|
@@ -105,6 +108,7 @@ tests/clingexplaid/res/transformed_program_assumptions_certain_signatures.lp
|
|
|
105
108
|
tests/clingexplaid/res/transformed_program_constraints.lp
|
|
106
109
|
tests/clingexplaid/res/transformed_program_constraints_id.lp
|
|
107
110
|
tests/clingexplaid/res/transformed_program_facts.lp
|
|
111
|
+
tests/clingexplaid/res/transformed_program_nothing_transformed.lp
|
|
108
112
|
tests/clingexplaid/res/transformed_program_optimization.lp
|
|
109
113
|
tests/clingexplaid/res/transformed_program_pattern.lp
|
|
110
114
|
tests/clingexplaid/res/transformed_program_rule_ids.lp
|
|
@@ -11,8 +11,7 @@ import clingo
|
|
|
11
11
|
|
|
12
12
|
from clingexplaid.mus import CoreComputer
|
|
13
13
|
from clingexplaid.mus.core_computer import UnsatisfiableSubset
|
|
14
|
-
from clingexplaid.
|
|
15
|
-
from clingexplaid.transformers.transformer_assumption import FilterPattern, FilterSignature
|
|
14
|
+
from clingexplaid.preprocessors import AssumptionPreprocessor, FilterPattern, FilterSignature
|
|
16
15
|
|
|
17
16
|
from .test_main import TEST_DIR
|
|
18
17
|
|
|
@@ -34,20 +33,18 @@ def get_mus_of_program(
|
|
|
34
33
|
|
|
35
34
|
ctl = control if control is not None else clingo.Control()
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
transformed_program =
|
|
36
|
+
ap = AssumptionPreprocessor(filters=assumption_filters)
|
|
37
|
+
transformed_program = ap.process(program_string)
|
|
39
38
|
|
|
40
39
|
ctl.add("base", [], transformed_program)
|
|
41
40
|
ctl.ground([("base", [])])
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
cc = CoreComputer(ctl, assumptions)
|
|
42
|
+
cc = CoreComputer(ctl, ap.assumptions)
|
|
46
43
|
|
|
47
44
|
def shrink_on_model(core: Sequence[int]) -> None:
|
|
48
45
|
_ = cc.shrink(core, timeout=timeout)
|
|
49
46
|
|
|
50
|
-
ctl.solve(assumptions=list(assumptions), on_core=shrink_on_model)
|
|
47
|
+
ctl.solve(assumptions=list(ap.assumptions), on_core=shrink_on_model)
|
|
51
48
|
|
|
52
49
|
# if the instance was satisfiable and the on_core function wasn't called an empty set is returned, else the mus.
|
|
53
50
|
result = cc.minimal if cc.minimal is not None else UnsatisfiableSubset(set())
|
|
@@ -223,11 +220,12 @@ class TestMUS(TestCase):
|
|
|
223
220
|
ctl = clingo.Control()
|
|
224
221
|
|
|
225
222
|
program_path = TEST_DIR.joinpath("res/test_program_multi_mus.lp")
|
|
226
|
-
|
|
227
|
-
|
|
223
|
+
ap = AssumptionPreprocessor(filters={FilterSignature("a", 1)})
|
|
224
|
+
with open(program_path, "r", encoding="utf-8") as file:
|
|
225
|
+
parsed = ap.process(file.read())
|
|
228
226
|
ctl.add("base", [], parsed)
|
|
229
227
|
ctl.ground([("base", [])])
|
|
230
|
-
cc = CoreComputer(ctl,
|
|
228
|
+
cc = CoreComputer(ctl, ap.assumptions)
|
|
231
229
|
|
|
232
230
|
mus_generator = cc.get_multiple_minimal()
|
|
233
231
|
|
|
@@ -246,11 +244,12 @@ class TestMUS(TestCase):
|
|
|
246
244
|
ctl = clingo.Control()
|
|
247
245
|
|
|
248
246
|
program_path = TEST_DIR.joinpath("res/test_program_multi_mus.lp")
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
ap = AssumptionPreprocessor(filters={FilterSignature("a", 1)})
|
|
248
|
+
with open(program_path, "r", encoding="utf-8") as file:
|
|
249
|
+
parsed = ap.process(file.read())
|
|
251
250
|
ctl.add("base", [], parsed)
|
|
252
251
|
ctl.ground([("base", [])])
|
|
253
|
-
cc = CoreComputer(ctl,
|
|
252
|
+
cc = CoreComputer(ctl, ap.assumptions)
|
|
254
253
|
|
|
255
254
|
mus_generator = cc.get_multiple_minimal(max_mus=2)
|
|
256
255
|
|