flamapy-sat 2.0.0.dev8__tar.gz → 2.1.0.dev0__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.
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/PKG-INFO +1 -1
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/models/pysat_diagnosis_model.py +16 -18
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/__init__.py +2 -2
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/__init__.py +1 -1
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/checker.py +1 -1
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/fastdiag.py +1 -1
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/hsdag/__init__.py +1 -1
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/hsdag/labeler/__init__.py +6 -6
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/pysat_abstract_identifier.py +1 -1
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/transformations/fm_to_diag_pysat.py +1 -1
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/models/pysat_model.py +9 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/models/txtcnf_model.py +6 -4
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_commonality.py +1 -1
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_false_optional_features.py +2 -2
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_filter.py +20 -8
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_metrics.py +17 -9
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_sampling.py +6 -6
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_satisfiable_configuration.py +6 -8
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/transformations/__init__.py +2 -2
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/transformations/cnf_to_pysat.py +9 -4
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/transformations/fm_to_pysat.py +20 -6
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy_sat.egg-info/PKG-INFO +1 -1
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy_sat.egg-info/SOURCES.txt +1 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy_sat.egg-info/requires.txt +2 -2
- flamapy-sat-2.1.0.dev0/pyproject.toml +53 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/setup.py +1 -1
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/README.md +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/__init__.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/models/__init__.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/hsdag/hsdag.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/hsdag/labeler/fastdiag_labeler.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/hsdag/labeler/labeler.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/hsdag/labeler/quickxplain_labeler.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/hsdag/node.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/quickxplain.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/utils.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/pysat_conflict.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/pysat_diagnosis.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/transformations/__init__.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/__init__.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/models/__init__.py +1 -1
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/__init__.py +4 -4
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_configurations.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_configurations_number.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_core_features.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_dead_features.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_satisfiable.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/transformations/dimacs_reader.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/transformations/dimacs_writer.py +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy_sat.egg-info/dependency_links.txt +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy_sat.egg-info/top_level.txt +0 -0
- {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/setup.cfg +0 -0
|
@@ -90,17 +90,16 @@ class DiagnosisModel(PySATModel):
|
|
|
90
90
|
# C = configuration
|
|
91
91
|
# B = {f0 = true} + CF (i.e., = PySATModel)
|
|
92
92
|
self._prepare_assumptions(configuration=configuration)
|
|
93
|
+
elif test_case is None:
|
|
94
|
+
# Diagnosis the feature model
|
|
95
|
+
# C = CF (i.e., = PySATModel - {f0 = true})
|
|
96
|
+
# B = {f0 = true}
|
|
97
|
+
self._prepare_assumptions()
|
|
93
98
|
else:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
self._prepare_assumptions()
|
|
99
|
-
else:
|
|
100
|
-
# Diagnosis the error
|
|
101
|
-
# C = CF (i.e., = PySATModel - {f0 = true})
|
|
102
|
-
# B = {f0 = true} + test_case
|
|
103
|
-
self._prepare_assumptions(test_case=test_case)
|
|
99
|
+
# Diagnosis the error
|
|
100
|
+
# C = CF (i.e., = PySATModel - {f0 = true})
|
|
101
|
+
# B = {f0 = true} + test_case
|
|
102
|
+
self._prepare_assumptions(test_case=test_case)
|
|
104
103
|
|
|
105
104
|
def prepare_redundancy_detection_task(self) -> None:
|
|
106
105
|
"""
|
|
@@ -136,13 +135,12 @@ class DiagnosisModel(PySATModel):
|
|
|
136
135
|
if configuration is not None:
|
|
137
136
|
self.set_b = assumption[:start_id_configuration]
|
|
138
137
|
self.set_c = assumption[start_id_configuration:]
|
|
138
|
+
elif test_case is not None:
|
|
139
|
+
self.set_b = [assumption[0], *assumption[start_id_test_case:]]
|
|
140
|
+
self.set_c = assumption[1:start_id_test_case]
|
|
139
141
|
else:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
self.set_c = assumption[1:start_id_test_case]
|
|
143
|
-
else:
|
|
144
|
-
self.set_b = [assumption[0]]
|
|
145
|
-
self.set_c = assumption[1:]
|
|
142
|
+
self.set_b = [assumption[0]]
|
|
143
|
+
self.set_c = assumption[1:]
|
|
146
144
|
|
|
147
145
|
def _prepare_assumptions_for_kb(self, assumption: List[int], id_assumption: int) -> int:
|
|
148
146
|
cstr_map = self.constraint_map
|
|
@@ -167,7 +165,7 @@ class DiagnosisModel(PySATModel):
|
|
|
167
165
|
|
|
168
166
|
def _convert_keys_to_features(self, configuration: 'Configuration') -> 'Configuration':
|
|
169
167
|
new_elements = {Feature(key) if isinstance(key, str)
|
|
170
|
-
else key: value for key, value
|
|
168
|
+
else key: value for key, value
|
|
171
169
|
in configuration.elements.items()}
|
|
172
170
|
return Configuration(new_elements)
|
|
173
171
|
|
|
@@ -197,4 +195,4 @@ class DiagnosisModel(PySATModel):
|
|
|
197
195
|
|
|
198
196
|
id_assumption += 1
|
|
199
197
|
|
|
200
|
-
return id_assumption
|
|
198
|
+
return id_assumption
|
|
@@ -3,11 +3,11 @@ from .labeler import IHSLabelable, AbstractHSParameters, LabelerType
|
|
|
3
3
|
from .quickxplain_labeler import QuickXPlainParameters, QuickXPlainLabeler
|
|
4
4
|
|
|
5
5
|
__all__ = [
|
|
6
|
-
'IHSLabelable',
|
|
7
6
|
'AbstractHSParameters',
|
|
8
|
-
'LabelerType',
|
|
9
|
-
'FastDiagParameters',
|
|
10
7
|
'FastDiagLabeler',
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
|
|
8
|
+
'FastDiagParameters',
|
|
9
|
+
'IHSLabelable',
|
|
10
|
+
'LabelerType',
|
|
11
|
+
'QuickXPlainLabeler',
|
|
12
|
+
'QuickXPlainParameters'
|
|
13
|
+
]
|
|
@@ -25,3 +25,12 @@ class PySATModel(VariabilityModel):
|
|
|
25
25
|
|
|
26
26
|
def get_all_clauses(self) -> CNF:
|
|
27
27
|
return self._cnf
|
|
28
|
+
|
|
29
|
+
def __eq__(self, other: object) -> bool:
|
|
30
|
+
if other is None or not isinstance(other, PySATModel):
|
|
31
|
+
return False
|
|
32
|
+
return set(self.get_all_clauses().clauses) == set(other.get_all_clauses().clauses)
|
|
33
|
+
|
|
34
|
+
def __hash__(self) -> int:
|
|
35
|
+
clause_tuples = (tuple(clause) for clause in self.get_all_clauses().clauses)
|
|
36
|
+
return hash(frozenset(clause_tuples))
|
|
@@ -195,14 +195,16 @@ def extract_variables(cnf_formula: str) -> list[str]:
|
|
|
195
195
|
|
|
196
196
|
# Remove initial and final parenthesis
|
|
197
197
|
and_symbol_pattern = ' ' + cnf_notation.value[CNFLogicConnective.AND] + ' '
|
|
198
|
-
clauses =
|
|
199
|
-
|
|
198
|
+
clauses = [
|
|
199
|
+
clause[1:-1]
|
|
200
|
+
for clause in cnf_formula.split(and_symbol_pattern)
|
|
201
|
+
]
|
|
200
202
|
# Remove final parenthesis of last clause (because of the possible end of line: '\n')
|
|
201
203
|
if ')' in clauses[len(clauses) - 1]:
|
|
202
204
|
clauses[len(clauses) - 1] = clauses[len(clauses) - 1][:-1]
|
|
203
205
|
|
|
204
|
-
for _clause in clauses:
|
|
205
|
-
tokens = flamapy.metamodels.pysat_metamodel.operations.diagnosis.utils.split(' ') # type: ignore[name-defined] # pylint: disable=undefined-variable # noqa: F821
|
|
206
|
+
for _clause in clauses:
|
|
207
|
+
tokens = flamapy.metamodels.pysat_metamodel.operations.diagnosis.utils.split(' ') # type: ignore[name-defined] # pylint: disable=undefined-variable # noqa: F821
|
|
206
208
|
tokens = list(filter(lambda t: t != cnf_notation.value[CNFLogicConnective.OR], tokens))
|
|
207
209
|
for feature in tokens:
|
|
208
210
|
if feature == cnf_notation.value[CNFLogicConnective.NOT]:
|
|
@@ -24,7 +24,7 @@ class PySATCommonality(Commonality):
|
|
|
24
24
|
pysat_products_op.execute(model)
|
|
25
25
|
products = pysat_products_op.get_result()
|
|
26
26
|
|
|
27
|
-
feature =
|
|
27
|
+
feature = next(iter(self.configuration.elements.keys()))
|
|
28
28
|
|
|
29
29
|
count = 0
|
|
30
30
|
for product in products:
|
|
@@ -34,7 +34,7 @@ class PySATFalseOptionalFeatures(FalseOptionalFeatures):
|
|
|
34
34
|
try:
|
|
35
35
|
feature_model = cast(FeatureModel, sat_model.original_model)
|
|
36
36
|
except FlamaException:
|
|
37
|
-
LOGGER.exception("The transformation didn't attach the source model, "
|
|
37
|
+
LOGGER.exception("The transformation didn't attach the source model, "
|
|
38
38
|
"which is required for this operation.")
|
|
39
39
|
|
|
40
40
|
real_optional_features = [f for f in feature_model.get_features()
|
|
@@ -52,6 +52,6 @@ class PySATFalseOptionalFeatures(FalseOptionalFeatures):
|
|
|
52
52
|
assert variable is not None
|
|
53
53
|
satisfiable = self.solver.solve(assumptions=[parent_variable, -variable])
|
|
54
54
|
if not satisfiable:
|
|
55
|
-
result.append(feature)
|
|
55
|
+
result.append(feature.name)
|
|
56
56
|
self.solver.delete()
|
|
57
57
|
return result
|
|
@@ -24,20 +24,32 @@ class PySATFilter(Filter):
|
|
|
24
24
|
def set_configuration(self, configuration: Configuration) -> None:
|
|
25
25
|
self.configuration = configuration
|
|
26
26
|
|
|
27
|
-
def execute(self, model: VariabilityModel) -> 'PySATFilter':
|
|
27
|
+
def execute(self, model: VariabilityModel) -> 'PySATFilter': # noqa C901
|
|
28
28
|
model = cast(PySATModel, model)
|
|
29
29
|
|
|
30
30
|
for clause in model.get_all_clauses(): # AC es conjunto de conjuntos
|
|
31
31
|
self.solver.add_clause(clause) # añadimos la constraint
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
assumptions.append(variable)
|
|
33
|
+
if not self.configuration.is_full:
|
|
34
|
+
assumptions = []
|
|
35
|
+
for feature, selected in self.configuration.elements.items():
|
|
36
|
+
if selected:
|
|
37
|
+
assumptions.append(model.variables[feature])
|
|
39
38
|
else:
|
|
40
|
-
assumptions.append(-
|
|
39
|
+
assumptions.append(-model.variables[feature])
|
|
40
|
+
else:
|
|
41
|
+
missing_features = [feature for feature in self.configuration.elements.keys()
|
|
42
|
+
if feature not in model.variables.keys()]
|
|
43
|
+
if missing_features:
|
|
44
|
+
raise ValueError("The configuration contains features that are \
|
|
45
|
+
not present in the feature model.",
|
|
46
|
+
list(missing_features))
|
|
47
|
+
assumptions = []
|
|
48
|
+
for feature in model.features.values():
|
|
49
|
+
if self.configuration.elements.get(feature, False):
|
|
50
|
+
assumptions.append(model.variables[feature])
|
|
51
|
+
else:
|
|
52
|
+
assumptions.append(-model.variables[feature])
|
|
41
53
|
|
|
42
54
|
for solution in self.solver.enum_models(assumptions=assumptions):
|
|
43
55
|
product = []
|
|
@@ -29,11 +29,19 @@ class PySATMetrics(Metrics):
|
|
|
29
29
|
super().__init__()
|
|
30
30
|
self.model: Optional[VariabilityModel] = None
|
|
31
31
|
self.result: list[dict[str, Any]] = []
|
|
32
|
-
self.
|
|
32
|
+
self._model_type_extension = "pysat"
|
|
33
33
|
self._features: dict[int, str] = {}
|
|
34
34
|
self._common_features: list[Any] = []
|
|
35
35
|
self._dead_features: list[Any] = []
|
|
36
36
|
|
|
37
|
+
@property
|
|
38
|
+
def model_type_extension(self) -> str:
|
|
39
|
+
return self._model_type_extension
|
|
40
|
+
|
|
41
|
+
@model_type_extension.setter
|
|
42
|
+
def model_type_extension(self, ext: str) -> None:
|
|
43
|
+
self._model_type_extension = ext
|
|
44
|
+
|
|
37
45
|
def get_result(self) -> list[dict[str, Any]]:
|
|
38
46
|
return self.result
|
|
39
47
|
|
|
@@ -46,10 +54,10 @@ class PySATMetrics(Metrics):
|
|
|
46
54
|
self._dead_features = sat_operations.PySATDeadFeatures().execute(self.model).get_result()
|
|
47
55
|
# Get all methods that are marked with the metric_method decorator
|
|
48
56
|
metric_methods = [getattr(self, method_name) for method_name in dir(self)
|
|
49
|
-
if callable(getattr(self, method_name)) and
|
|
57
|
+
if callable(getattr(self, method_name)) and
|
|
50
58
|
hasattr(getattr(self, method_name), '_is_metric_method')]
|
|
51
59
|
if self.filter is not None:
|
|
52
|
-
metric_methods = [method for method in metric_methods
|
|
60
|
+
metric_methods = [method for method in metric_methods
|
|
53
61
|
if method.__name__ in self.filter]
|
|
54
62
|
|
|
55
63
|
return [method() for method in metric_methods]
|
|
@@ -61,7 +69,7 @@ class PySATMetrics(Metrics):
|
|
|
61
69
|
raise FlamaException('Model not initialized.')
|
|
62
70
|
name = "Satisfiable"
|
|
63
71
|
_satisfiable = sat_operations.PySATSatisfiable().execute(self.model).get_result()
|
|
64
|
-
result = self.construct_result(name=name,
|
|
72
|
+
result = self.construct_result(name=name,
|
|
65
73
|
doc=self.satisfiable.__doc__, result=_satisfiable)
|
|
66
74
|
return result
|
|
67
75
|
|
|
@@ -81,8 +89,8 @@ class PySATMetrics(Metrics):
|
|
|
81
89
|
def variant_features(self) -> dict[str, Any]:
|
|
82
90
|
"""Features that do not appear in all the configurations."""
|
|
83
91
|
name = "Variant features"
|
|
84
|
-
_variant_features = [f for f in self._features.values()
|
|
85
|
-
if f not in self._common_features and
|
|
92
|
+
_variant_features = [f for f in self._features.values()
|
|
93
|
+
if f not in self._common_features and
|
|
86
94
|
f not in self._dead_features]
|
|
87
95
|
result = self.construct_result(name=name,
|
|
88
96
|
doc=self.variant_features.__doc__,
|
|
@@ -122,7 +130,7 @@ class PySATMetrics(Metrics):
|
|
|
122
130
|
seen_once.add(feature)
|
|
123
131
|
|
|
124
132
|
# Step 3: Find features that are in seen_once but not in seen_multiple
|
|
125
|
-
_unique_features = seen_once - seen_multiple
|
|
133
|
+
_unique_features = list(seen_once - seen_multiple)
|
|
126
134
|
|
|
127
135
|
result = self.construct_result(name=name,
|
|
128
136
|
doc=self.unique_features.__doc__,
|
|
@@ -133,7 +141,7 @@ class PySATMetrics(Metrics):
|
|
|
133
141
|
|
|
134
142
|
@metric_method
|
|
135
143
|
def false_optional_features(self) -> dict[str, Any]:
|
|
136
|
-
"""Features defined as optionals the selection of their parents make the feature itself
|
|
144
|
+
"""Features defined as optionals the selection of their parents make the feature itself
|
|
137
145
|
selected as well."""
|
|
138
146
|
if self.model is None:
|
|
139
147
|
raise FlamaException('Model not initialized.')
|
|
@@ -148,7 +156,7 @@ class PySATMetrics(Metrics):
|
|
|
148
156
|
|
|
149
157
|
@metric_method
|
|
150
158
|
def configurations(self) -> dict[str, Any]:
|
|
151
|
-
"""Number of configurations represented by the feature model.
|
|
159
|
+
"""Number of configurations represented by the feature model.
|
|
152
160
|
|
|
153
161
|
If <= is shown, the number represents an upper estimation bound.
|
|
154
162
|
"""
|
|
@@ -37,17 +37,17 @@ class PySATSampling(Sampling):
|
|
|
37
37
|
|
|
38
38
|
def execute(self, model: VariabilityModel) -> 'PySATSampling':
|
|
39
39
|
sat_model = cast(PySATModel, model)
|
|
40
|
-
self.result = sample(self.solver,
|
|
41
|
-
sat_model,
|
|
42
|
-
self.sample_size,
|
|
43
|
-
self.with_replacement,
|
|
40
|
+
self.result = sample(self.solver,
|
|
41
|
+
sat_model,
|
|
42
|
+
self.sample_size,
|
|
43
|
+
self.with_replacement,
|
|
44
44
|
self.partial_configuration)
|
|
45
45
|
return self
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def sample(solver: Solver,
|
|
49
|
-
model: PySATModel,
|
|
50
|
-
sample_size: int,
|
|
49
|
+
model: PySATModel,
|
|
50
|
+
sample_size: int,
|
|
51
51
|
with_replacement: bool, # pylint: disable=unused-argument
|
|
52
52
|
partial_configuration: Optional[Configuration] # pylint: disable=unused-argument
|
|
53
53
|
) -> list[Configuration]:
|
|
@@ -14,7 +14,6 @@ class PySATSatisfiableConfiguration(SatisfiableConfiguration):
|
|
|
14
14
|
self.result = False
|
|
15
15
|
self.configuration = Configuration(elements={})
|
|
16
16
|
self.solver = Solver(name='glucose3')
|
|
17
|
-
self.is_full = False
|
|
18
17
|
|
|
19
18
|
def is_satisfiable(self) -> bool:
|
|
20
19
|
return self.result
|
|
@@ -22,9 +21,8 @@ class PySATSatisfiableConfiguration(SatisfiableConfiguration):
|
|
|
22
21
|
def get_result(self) -> bool:
|
|
23
22
|
return self.is_satisfiable()
|
|
24
23
|
|
|
25
|
-
def set_configuration(self, configuration: Configuration
|
|
24
|
+
def set_configuration(self, configuration: Configuration) -> None:
|
|
26
25
|
self.configuration = configuration
|
|
27
|
-
self.is_full = is_full
|
|
28
26
|
|
|
29
27
|
def execute(self, model: VariabilityModel) -> 'PySATSatisfiableConfiguration':
|
|
30
28
|
sat_model = cast(PySATModel, model)
|
|
@@ -32,7 +30,7 @@ class PySATSatisfiableConfiguration(SatisfiableConfiguration):
|
|
|
32
30
|
for clause in sat_model.get_all_clauses(): # AC es conjunto de conjuntos
|
|
33
31
|
self.solver.add_clause(clause) # añadimos la constraint
|
|
34
32
|
|
|
35
|
-
if not self.is_full:
|
|
33
|
+
if not self.configuration.is_full:
|
|
36
34
|
assumptions = []
|
|
37
35
|
for feature, selected in self.configuration.elements.items():
|
|
38
36
|
if selected:
|
|
@@ -40,12 +38,12 @@ class PySATSatisfiableConfiguration(SatisfiableConfiguration):
|
|
|
40
38
|
else:
|
|
41
39
|
assumptions.append(-sat_model.variables[feature])
|
|
42
40
|
else:
|
|
43
|
-
missing_features = [feature for feature in self.configuration.elements.keys()
|
|
41
|
+
missing_features = [feature for feature in self.configuration.elements.keys()
|
|
44
42
|
if feature not in sat_model.variables.keys()]
|
|
45
43
|
|
|
46
44
|
if missing_features:
|
|
47
45
|
print("The features that are missing are:", list(missing_features))
|
|
48
|
-
print("The feature model contains the following features:",
|
|
46
|
+
print("The feature model contains the following features:",
|
|
49
47
|
list(sat_model.variables.keys()))
|
|
50
48
|
self.result = False
|
|
51
49
|
return self
|
|
@@ -53,11 +51,11 @@ class PySATSatisfiableConfiguration(SatisfiableConfiguration):
|
|
|
53
51
|
assumptions = []
|
|
54
52
|
for feature in sat_model.features.values():
|
|
55
53
|
|
|
56
|
-
if
|
|
54
|
+
if self.configuration.elements.get(feature, False):
|
|
57
55
|
assumptions.append(sat_model.variables[feature])
|
|
58
56
|
else:
|
|
59
57
|
assumptions.append(-sat_model.variables[feature])
|
|
60
58
|
|
|
61
59
|
self.result = self.solver.solve(assumptions=assumptions)
|
|
62
60
|
self.solver.delete()
|
|
63
|
-
return self
|
|
61
|
+
return self
|
|
@@ -53,15 +53,17 @@ class CNFReader(TextToModel):
|
|
|
53
53
|
|
|
54
54
|
def _extract_clauses(self, cnf_formula: str, cnf_notation: TextCNFNotation) -> None:
|
|
55
55
|
and_symbol_pattern = ' ' + cnf_notation.value[CNFLogicConnective.AND] + ' '
|
|
56
|
-
clauses = list(map(lambda c: c[1:len(c) - 1], cnf_formula.split(and_symbol_pattern)))
|
|
57
56
|
# Remove initial and final parenthesis
|
|
58
|
-
|
|
57
|
+
clauses = [
|
|
58
|
+
clause[1:-1]
|
|
59
|
+
for clause in cnf_formula.split(and_symbol_pattern)
|
|
60
|
+
]
|
|
59
61
|
# Remove final parenthesis of last clause (because of the possible end of line: '\n')
|
|
60
62
|
if ')' in clauses[len(clauses) - 1]:
|
|
61
63
|
clauses[len(clauses) - 1] = clauses[len(clauses) - 1][:-1]
|
|
62
64
|
|
|
63
65
|
for _c in clauses:
|
|
64
|
-
tokens = flamapy.metamodels.pysat_metamodel.operations.diagnosis.utils.split(' ') # type: ignore[name-defined] # noqa: F821
|
|
66
|
+
tokens = flamapy.metamodels.pysat_metamodel.operations.diagnosis.utils.split(' ') # type: ignore[name-defined] # noqa: F821
|
|
65
67
|
tokens = list(filter(lambda t: t != cnf_notation.value[CNFLogicConnective.OR], tokens))
|
|
66
68
|
logic_not = False
|
|
67
69
|
cnf_clause = []
|
|
@@ -69,7 +71,10 @@ class CNFReader(TextToModel):
|
|
|
69
71
|
if feature == cnf_notation.value[CNFLogicConnective.NOT]:
|
|
70
72
|
logic_not = True
|
|
71
73
|
elif feature.startswith(cnf_notation.value[CNFLogicConnective.NOT]):
|
|
72
|
-
feature = feature.replace(
|
|
74
|
+
feature = feature.replace( #noqa: PLW2901
|
|
75
|
+
cnf_notation.value[CNFLogicConnective.NOT],
|
|
76
|
+
'', 1
|
|
77
|
+
)
|
|
73
78
|
self._add_feature(feature)
|
|
74
79
|
cnf_clause.append(-1 * self.destination_model.variables[feature])
|
|
75
80
|
else:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import itertools
|
|
2
|
+
import copy
|
|
2
3
|
from typing import Any, List
|
|
3
4
|
|
|
4
5
|
from flamapy.core.transformations import ModelToModel
|
|
@@ -8,6 +9,10 @@ from flamapy.metamodels.fm_metamodel.models.feature_model import (
|
|
|
8
9
|
Feature,
|
|
9
10
|
Relation,
|
|
10
11
|
)
|
|
12
|
+
from flamapy.metamodels.fm_metamodel.transformations.refactorings import (
|
|
13
|
+
FeatureCardinalityRefactoring
|
|
14
|
+
)
|
|
15
|
+
from flamapy.metamodels.fm_metamodel.transformations import FlatFM
|
|
11
16
|
from flamapy.metamodels.pysat_metamodel.models.pysat_model import PySATModel
|
|
12
17
|
|
|
13
18
|
|
|
@@ -139,9 +144,9 @@ class FmToPysat(ModelToModel):
|
|
|
139
144
|
clauses = self._add_mandatory_relation(relation)
|
|
140
145
|
elif relation.is_optional():
|
|
141
146
|
clauses = self._add_optional_relation(relation)
|
|
142
|
-
elif relation.is_or():
|
|
147
|
+
elif relation.is_or():
|
|
143
148
|
clauses = self._add_or_relation(relation)
|
|
144
|
-
elif relation.is_alternative():
|
|
149
|
+
elif relation.is_alternative():
|
|
145
150
|
clauses = self._add_alternative_relation(relation)
|
|
146
151
|
else:
|
|
147
152
|
clauses = self._add_constraint_relation(relation)
|
|
@@ -160,15 +165,24 @@ class FmToPysat(ModelToModel):
|
|
|
160
165
|
self.destination_model.add_clause(clause_variables)
|
|
161
166
|
|
|
162
167
|
def transform(self) -> PySATModel:
|
|
163
|
-
|
|
168
|
+
# FlatFM if the feature model contains imports
|
|
169
|
+
feature_model = self.source_model
|
|
170
|
+
if feature_model.imports:
|
|
171
|
+
feature_model = FlatFM(feature_model).transform()
|
|
172
|
+
# Apply the feature cardinality refactoring to the source model
|
|
173
|
+
if FeatureCardinalityRefactoring(feature_model).is_applicable():
|
|
174
|
+
feature_model = copy.deepcopy(feature_model)
|
|
175
|
+
feature_model = FeatureCardinalityRefactoring(feature_model).transform()
|
|
176
|
+
|
|
177
|
+
for feature in feature_model.get_features():
|
|
164
178
|
self.add_feature(feature)
|
|
165
179
|
|
|
166
|
-
self.add_root(
|
|
180
|
+
self.add_root(feature_model.root)
|
|
167
181
|
|
|
168
|
-
for relation in
|
|
182
|
+
for relation in feature_model.get_relations():
|
|
169
183
|
self.add_relation(relation)
|
|
170
184
|
|
|
171
|
-
for constraint in
|
|
185
|
+
for constraint in feature_model.get_logical_constraints():
|
|
172
186
|
self.add_constraint(constraint)
|
|
173
187
|
|
|
174
188
|
return self.destination_model
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[tool.ruff]
|
|
2
|
+
line-length = 100 # Matches max-line-length from pycodestyle
|
|
3
|
+
target-version = "py38" # Adjust if using another Python version
|
|
4
|
+
|
|
5
|
+
[tool.ruff.lint]
|
|
6
|
+
select = ["E", "W", "F", "C", "PL", "RUF"] # Includes pycodestyle, pylint, and McCabe complexity
|
|
7
|
+
ignore = [
|
|
8
|
+
"RUF012", # Equivalent to Pylint's super-init-not-called
|
|
9
|
+
"RUF002", # Weird symbols on docstring
|
|
10
|
+
"RUF001", # Weird symbols string
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[tool.ruff.lint.mccabe]
|
|
14
|
+
max-complexity = 10 # Equivalent to enabling mccabe in Prospector
|
|
15
|
+
|
|
16
|
+
[tool.ruff.lint.per-file-ignores]
|
|
17
|
+
"flamapy/metamodels/fm_metamodel/transformations/pysat_to_fm.py" = ["ALL"] # Ignore this file entirely
|
|
18
|
+
"tests/*" = ["ALL"] # Ignore all test files
|
|
19
|
+
"build/*" = ["ALL"]
|
|
20
|
+
"resources/*" = ["ALL"]
|
|
21
|
+
".mypy_cache/*" = ["ALL"]
|
|
22
|
+
"venv/*" = ["ALL"]
|
|
23
|
+
".venv/*" = ["ALL"]
|
|
24
|
+
"env/*" = ["ALL"]
|
|
25
|
+
"flamapy_fm.egg-info/*" = ["ALL"]
|
|
26
|
+
"__pycache__/*" = ["ALL"]
|
|
27
|
+
|
|
28
|
+
[tool.ruff.lint.pycodestyle]
|
|
29
|
+
max-line-length = 100 # Matches your existing pycodestyle setting
|
|
30
|
+
|
|
31
|
+
# MYPY CONFIGURATION
|
|
32
|
+
[tool.mypy]
|
|
33
|
+
scripts_are_modules = true
|
|
34
|
+
show_traceback = true
|
|
35
|
+
|
|
36
|
+
# Strict checking
|
|
37
|
+
check_untyped_defs = true
|
|
38
|
+
disallow_untyped_defs = true
|
|
39
|
+
disallow_any_generics = true
|
|
40
|
+
warn_no_return = true
|
|
41
|
+
strict_optional = true
|
|
42
|
+
no_implicit_optional = true
|
|
43
|
+
warn_redundant_casts = true
|
|
44
|
+
warn_unused_ignores = true
|
|
45
|
+
|
|
46
|
+
# Show error codes for type: ignore comments
|
|
47
|
+
show_error_codes = true
|
|
48
|
+
|
|
49
|
+
# Suppress missing imports errors
|
|
50
|
+
ignore_missing_imports = true
|
|
51
|
+
|
|
52
|
+
# Warn about unreachable or redundant code
|
|
53
|
+
warn_unreachable = true
|
|
@@ -16,7 +16,7 @@ dev_requirements = read_requirements("requirements-dev.txt")
|
|
|
16
16
|
|
|
17
17
|
setuptools.setup(
|
|
18
18
|
name="flamapy-sat",
|
|
19
|
-
version="2.
|
|
19
|
+
version="2.1.0.dev0",
|
|
20
20
|
author="Flamapy",
|
|
21
21
|
author_email="flamapy@us.es",
|
|
22
22
|
description="flamapy-sat is a plugin to flamapy module",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/__init__.py
RENAMED
|
File without changes
|
|
@@ -11,14 +11,14 @@ from .pysat_metrics import PySATMetrics
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
__all__ = [
|
|
14
|
-
'
|
|
15
|
-
'PySATSatisfiableConfiguration',
|
|
14
|
+
'PySATCommonality',
|
|
16
15
|
'PySATConfigurations',
|
|
17
16
|
'PySATConfigurationsNumber',
|
|
18
|
-
'PySATCommonality',
|
|
19
|
-
'PySATFilter',
|
|
20
17
|
'PySATCoreFeatures',
|
|
21
18
|
'PySATDeadFeatures',
|
|
22
19
|
'PySATFalseOptionalFeatures',
|
|
20
|
+
'PySATFilter',
|
|
23
21
|
'PySATMetrics',
|
|
22
|
+
'PySATSatisfiable',
|
|
23
|
+
'PySATSatisfiableConfiguration',
|
|
24
24
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|