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.
Files changed (52) hide show
  1. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/PKG-INFO +1 -1
  2. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/models/pysat_diagnosis_model.py +16 -18
  3. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/__init__.py +2 -2
  4. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/__init__.py +1 -1
  5. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/checker.py +1 -1
  6. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/fastdiag.py +1 -1
  7. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/hsdag/__init__.py +1 -1
  8. {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
  9. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/pysat_abstract_identifier.py +1 -1
  10. {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
  11. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/models/pysat_model.py +9 -0
  12. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/models/txtcnf_model.py +6 -4
  13. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_commonality.py +1 -1
  14. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_false_optional_features.py +2 -2
  15. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_filter.py +20 -8
  16. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_metrics.py +17 -9
  17. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_sampling.py +6 -6
  18. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_satisfiable_configuration.py +6 -8
  19. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/transformations/__init__.py +2 -2
  20. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/transformations/cnf_to_pysat.py +9 -4
  21. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/transformations/fm_to_pysat.py +20 -6
  22. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy_sat.egg-info/PKG-INFO +1 -1
  23. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy_sat.egg-info/SOURCES.txt +1 -0
  24. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy_sat.egg-info/requires.txt +2 -2
  25. flamapy-sat-2.1.0.dev0/pyproject.toml +53 -0
  26. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/setup.py +1 -1
  27. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/README.md +0 -0
  28. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/__init__.py +0 -0
  29. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/models/__init__.py +0 -0
  30. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/hsdag/hsdag.py +0 -0
  31. {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
  32. {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
  33. {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
  34. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/hsdag/node.py +0 -0
  35. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/quickxplain.py +0 -0
  36. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/diagnosis/utils.py +0 -0
  37. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/pysat_conflict.py +0 -0
  38. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/operations/pysat_diagnosis.py +0 -0
  39. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_diagnosis_metamodel/transformations/__init__.py +0 -0
  40. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/__init__.py +0 -0
  41. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/models/__init__.py +1 -1
  42. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/__init__.py +4 -4
  43. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_configurations.py +0 -0
  44. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_configurations_number.py +0 -0
  45. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_core_features.py +0 -0
  46. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_dead_features.py +0 -0
  47. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/operations/pysat_satisfiable.py +0 -0
  48. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/transformations/dimacs_reader.py +0 -0
  49. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy/metamodels/pysat_metamodel/transformations/dimacs_writer.py +0 -0
  50. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy_sat.egg-info/dependency_links.txt +0 -0
  51. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/flamapy_sat.egg-info/top_level.txt +0 -0
  52. {flamapy-sat-2.0.0.dev8 → flamapy-sat-2.1.0.dev0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flamapy-sat
3
- Version: 2.0.0.dev8
3
+ Version: 2.1.0.dev0
4
4
  Summary: flamapy-sat is a plugin to flamapy module
5
5
  Home-page: https://github.com/flamapy/pysat_metamodel
6
6
  Author: Flamapy
@@ -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
- if test_case is None:
95
- # Diagnosis the feature model
96
- # C = CF (i.e., = PySATModel - {f0 = true})
97
- # B = {f0 = true}
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
- if test_case is not None:
141
- self.set_b = [assumption[0]] + assumption[start_id_test_case:]
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
@@ -7,6 +7,6 @@ from .pysat_diagnosis import PySATDiagnosis
7
7
 
8
8
  __all__ = [
9
9
  'PySATAbstractIdentifier',
10
- 'PySATDiagnosis',
11
- 'PySATConflict'
10
+ 'PySATConflict',
11
+ 'PySATDiagnosis'
12
12
  ]
@@ -6,4 +6,4 @@ __all__ = [
6
6
  "ConsistencyChecker",
7
7
  "FastDiag",
8
8
  "QuickXPlain"
9
- ]
9
+ ]
@@ -27,4 +27,4 @@ class ConsistencyChecker:
27
27
  return self.result
28
28
 
29
29
  def delete(self) -> None:
30
- self.solver.delete()
30
+ self.solver.delete()
@@ -92,4 +92,4 @@ class FastDiag:
92
92
  logging.debug('<<< return [Δ1={Δ1} ∪ Δ2={Δ2}]')
93
93
 
94
94
  # return Δ1 + Δ2
95
- return delta1 + delta2
95
+ return delta1 + delta2
@@ -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
- 'QuickXPlainParameters',
12
- 'QuickXPlainLabeler'
13
- ]
8
+ 'FastDiagParameters',
9
+ 'IHSLabelable',
10
+ 'LabelerType',
11
+ 'QuickXPlainLabeler',
12
+ 'QuickXPlainParameters'
13
+ ]
@@ -83,4 +83,4 @@ class PySATAbstractIdentifier(Operation):
83
83
 
84
84
  @abstractmethod
85
85
  def set_result_messages(self, cs_mess: str, diag_mess: str) -> None:
86
- pass
86
+ pass
@@ -70,4 +70,4 @@ class FmToDiagPysat(FmToPysat):
70
70
  ctc_clauses.append(clause_variables)
71
71
  self.destination_model.add_clause(clause_variables)
72
72
 
73
- self.destination_model.add_clause_to_map(str(ctc), ctc_clauses)
73
+ self.destination_model.add_clause_to_map(str(ctc), ctc_clauses)
@@ -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 = list(map(lambda c: c[1:len(c) - 1], cnf_formula.split(and_symbol_pattern)))
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, E501
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 = list(self.configuration.elements.keys())[0]
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
- assumptions = []
34
- for feat in self.configuration.elements.items():
35
- variable = model.variables.get(feat[0])
36
- if variable is not None:
37
- if feat[1]:
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(-variable)
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.model_type_extension = "pysat"
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, is_full: bool) -> None:
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 feature in self.configuration.elements.keys():
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
@@ -5,8 +5,8 @@ from .dimacs_writer import DimacsWriter
5
5
 
6
6
 
7
7
  __all__ = [
8
- 'FmToPysat',
9
8
  'CNFReader',
10
9
  'DimacsReader',
11
- 'DimacsWriter'
10
+ 'DimacsWriter',
11
+ 'FmToPysat'
12
12
  ]
@@ -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, E501
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(cnf_notation.value[CNFLogicConnective.NOT], '', 1)
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
- for feature in self.source_model.get_features():
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(self.source_model.root)
180
+ self.add_root(feature_model.root)
167
181
 
168
- for relation in self.source_model.get_relations():
182
+ for relation in feature_model.get_relations():
169
183
  self.add_relation(relation)
170
184
 
171
- for constraint in self.source_model.get_constraints():
185
+ for constraint in feature_model.get_logical_constraints():
172
186
  self.add_constraint(constraint)
173
187
 
174
188
  return self.destination_model
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flamapy-sat
3
- Version: 2.0.0.dev8
3
+ Version: 2.1.0.dev0
4
4
  Summary: flamapy-sat is a plugin to flamapy module
5
5
  Home-page: https://github.com/flamapy/pysat_metamodel
6
6
  Author: Flamapy
@@ -1,4 +1,5 @@
1
1
  README.md
2
+ pyproject.toml
2
3
  setup.py
3
4
  flamapy/metamodels/pysat_diagnosis_metamodel/__init__.py
4
5
  flamapy/metamodels/pysat_diagnosis_metamodel/models/__init__.py
@@ -1,5 +1,5 @@
1
- flamapy-fw==2.0.0.dev7
2
- flamapy-fm==2.0.0.dev8
1
+ flamapy-fw~=2.1.0.dev0
2
+ flamapy-fm~=2.1.0.dev0
3
3
  python-sat~=0.1.7.dev1
4
4
 
5
5
  [dev]
@@ -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.0.0.dev8",
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",
@@ -9,8 +9,8 @@ from .txtcnf_model import (
9
9
 
10
10
 
11
11
  __all__ = [
12
+ 'CNFLogicConnective',
12
13
  'PySATModel',
13
14
  'TextCNFModel',
14
- 'CNFLogicConnective',
15
15
  'TextCNFNotation'
16
16
  ]
@@ -11,14 +11,14 @@ from .pysat_metrics import PySATMetrics
11
11
 
12
12
 
13
13
  __all__ = [
14
- 'PySATSatisfiable',
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
  ]