flamapy-fw 2.6.0.dev5__tar.gz → 2.6.0.dev11__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 (66) hide show
  1. {flamapy_fw-2.6.0.dev5/flamapy_fw.egg-info → flamapy_fw-2.6.0.dev11}/PKG-INFO +1 -1
  2. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/discover.py +8 -0
  3. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/__init__.py +4 -0
  4. flamapy_fw-2.6.0.dev11/flamapy/core/operations/attribute_optimization.py +68 -0
  5. flamapy_fw-2.6.0.dev11/flamapy/core/operations/configurations.py +29 -0
  6. flamapy_fw-2.6.0.dev11/flamapy/core/operations/configurations_number.py +30 -0
  7. flamapy_fw-2.6.0.dev11/flamapy/core/operations/core_features.py +29 -0
  8. flamapy_fw-2.6.0.dev11/flamapy/core/operations/dead_features.py +29 -0
  9. flamapy_fw-2.6.0.dev11/flamapy/core/operations/descriptor.py +61 -0
  10. flamapy_fw-2.6.0.dev11/flamapy/core/operations/false_optional_features.py +33 -0
  11. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/sampling.py +19 -0
  12. flamapy_fw-2.6.0.dev11/flamapy/core/operations/satisfiable.py +28 -0
  13. flamapy_fw-2.6.0.dev11/flamapy/core/operations/satisfiable_configuration.py +47 -0
  14. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11/flamapy_fw.egg-info}/PKG-INFO +1 -1
  15. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy_fw.egg-info/SOURCES.txt +1 -0
  16. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/pyproject.toml +1 -1
  17. flamapy_fw-2.6.0.dev5/flamapy/core/operations/attribute_optimization.py +0 -30
  18. flamapy_fw-2.6.0.dev5/flamapy/core/operations/configurations.py +0 -14
  19. flamapy_fw-2.6.0.dev5/flamapy/core/operations/configurations_number.py +0 -13
  20. flamapy_fw-2.6.0.dev5/flamapy/core/operations/core_features.py +0 -14
  21. flamapy_fw-2.6.0.dev5/flamapy/core/operations/dead_features.py +0 -14
  22. flamapy_fw-2.6.0.dev5/flamapy/core/operations/false_optional_features.py +0 -17
  23. flamapy_fw-2.6.0.dev5/flamapy/core/operations/satisfiable.py +0 -13
  24. flamapy_fw-2.6.0.dev5/flamapy/core/operations/satisfiable_configuration.py +0 -18
  25. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/LICENSE +0 -0
  26. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/README.md +0 -0
  27. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/__init__.py +0 -0
  28. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/config.py +0 -0
  29. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/exceptions.py +0 -0
  30. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/models/__init__.py +0 -0
  31. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/models/ast.py +0 -0
  32. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/models/variability_model.py +0 -0
  33. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/abstract_operation.py +0 -0
  34. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/atomic_sets.py +0 -0
  35. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/average_branching_factor.py +0 -0
  36. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/commonality.py +0 -0
  37. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/count_leafs.py +0 -0
  38. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/error_detection.py +0 -0
  39. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/error_diagnosis.py +0 -0
  40. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/estimated_configurations_number.py +0 -0
  41. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/filter.py +0 -0
  42. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/metrics_operation.py +0 -0
  43. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/operations/variability.py +0 -0
  44. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/plugins.py +0 -0
  45. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/transformations/__init__.py +0 -0
  46. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/transformations/abstract_transformation.py +0 -0
  47. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/transformations/model_to_model.py +0 -0
  48. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/transformations/model_to_text.py +0 -0
  49. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/transformations/text_to_model.py +0 -0
  50. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/core/utils.py +0 -0
  51. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/metamodels/configuration_metamodel/__init__.py +0 -0
  52. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/metamodels/configuration_metamodel/models/__init__.py +0 -0
  53. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/metamodels/configuration_metamodel/models/configuration.py +0 -0
  54. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/metamodels/configuration_metamodel/transformations/__init__.py +0 -0
  55. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/metamodels/configuration_metamodel/transformations/configuration_basic_reader.py +0 -0
  56. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/metamodels/configuration_metamodel/transformations/configuration_basic_writer.py +0 -0
  57. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/metamodels/configuration_metamodel/transformations/configuration_json_reader.py +0 -0
  58. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/metamodels/configuration_metamodel/transformations/configuration_json_writer.py +0 -0
  59. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/metamodels/configuration_metamodel/transformations/uvls_json_reader.py +0 -0
  60. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy/metamodels/configuration_metamodel/transformations/uvls_json_writer.py +0 -0
  61. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy_fw.egg-info/dependency_links.txt +0 -0
  62. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy_fw.egg-info/entry_points.txt +0 -0
  63. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy_fw.egg-info/requires.txt +0 -0
  64. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/flamapy_fw.egg-info/top_level.txt +0 -0
  65. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/setup.cfg +0 -0
  66. {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev11}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flamapy-fw
3
- Version: 2.6.0.dev5
3
+ Version: 2.6.0.dev11
4
4
  Summary: Flamapy is a Python-based AAFM framework that takes into consideration previous AAFM tool designs and enables multi-solver and multi-metamodel support for the integration of AAFM tooling on the Python ecosystem.
5
5
  Author-email: Flamapy <flamapy@us.es>
6
6
  License-Expression: GPL-3.0-or-later
@@ -11,6 +11,7 @@ from flamapy.core.exceptions import TransformationNotFound
11
11
  from flamapy.core.exceptions import ConfigurationNotFound
12
12
  from flamapy.core.models import VariabilityModel
13
13
  from flamapy.core.operations import Operation
14
+ from flamapy.core.operations.descriptor import OperationDescriptor, collect_descriptors
14
15
  from flamapy.core.plugins import Operations, Plugin, Plugins
15
16
  from flamapy.core.transformations import Transformation
16
17
  from flamapy.core.transformations.text_to_model import TextToModel
@@ -104,6 +105,13 @@ class DiscoverMetamodels:
104
105
 
105
106
  return operations
106
107
 
108
+ def available_operations(self) -> dict[str, OperationDescriptor]:
109
+ """Facade descriptors of every discovered operation that declares one, keyed by public name.
110
+
111
+ This is the single source the facade, CLI and REST derive their operation surface from.
112
+ """
113
+ return collect_descriptors(self.get_operations())
114
+
107
115
  def get_transformations(self) -> list[Type[Transformation]]:
108
116
  """Get transformations for all modules"""
109
117
  transformations: list[Type[Transformation]] = []
@@ -21,6 +21,7 @@ from .attribute_optimization import ( # pylint: disable=cyclic-import
21
21
  AttributeOptimization,
22
22
  OptimizationGoal,
23
23
  )
24
+ from .descriptor import Input, OperationDescriptor, collect_descriptors
24
25
 
25
26
  __all__ = [
26
27
  "AtomicSets",
@@ -37,11 +38,14 @@ __all__ = [
37
38
  "EstimatedConfigurationsNumber",
38
39
  "FalseOptionalFeatures",
39
40
  "Filter",
41
+ "Input",
40
42
  "Metrics",
41
43
  "Operation",
44
+ "OperationDescriptor",
42
45
  "OptimizationGoal",
43
46
  "Sampling",
44
47
  "Satisfiable",
45
48
  "SatisfiableConfiguration",
46
49
  "Variability",
50
+ "collect_descriptors",
47
51
  ]
@@ -0,0 +1,68 @@
1
+ from abc import abstractmethod
2
+ from enum import Enum
3
+ from typing import Any
4
+
5
+ from flamapy.core.operations import Operation
6
+ from flamapy.core.operations.descriptor import OperationDescriptor, Input
7
+
8
+
9
+ class OptimizationGoal(Enum):
10
+ MAXIMIZE = 'Maximize'
11
+ MINIMIZE = 'Minimize'
12
+
13
+
14
+ def _attribute_optimization_inputs(operation: Any, facade: Any, kwargs: dict[str, Any]) -> None:
15
+ objectives = kwargs['objectives']
16
+ if isinstance(objectives, str):
17
+ attributes = {objectives: OptimizationGoal.MINIMIZE}
18
+ else:
19
+ attributes = {
20
+ name: (OptimizationGoal.MINIMIZE if str(goal).lower().startswith('min')
21
+ else OptimizationGoal.MAXIMIZE)
22
+ for name, goal in dict(objectives).items()
23
+ }
24
+ operation.set_attributes(attributes)
25
+
26
+
27
+ def _attribute_optimization_result(result: Any) -> Any:
28
+ # z3 returns (configuration, values) tuples; sat returns bare configurations.
29
+ return [item[0] if isinstance(item, tuple) else item for item in result]
30
+
31
+
32
+ class AttributeOptimization(Operation):
33
+ """Return the configuration(s) that optimize the given numeric feature attribute(s).
34
+
35
+ Attributes are identified by name and mapped to an :class:`OptimizationGoal`. The
36
+ objective for each attribute is the sum of its values over the selected features.
37
+ """
38
+
39
+ facade = OperationDescriptor(
40
+ doc=(
41
+ 'Returns the configuration(s) that optimize one or more numeric feature\n'
42
+ 'attributes.\n'
43
+ '\n'
44
+ '``objectives`` is either a single attribute name (defaults to minimizing it)\n'
45
+ 'or a mapping ``{attribute_name: "Minimize"|"Maximize"}``. ``backend`` selects\n'
46
+ 'the plugin: "sat" performs single-objective MaxSAT optimization; "z3"\n'
47
+ 'additionally supports typed attributes and multi-objective (Pareto)\n'
48
+ 'optimization. Defaults to "sat".'
49
+ ),
50
+ returns='Union[None, List[Configuration]]',
51
+ name='attribute_optimization', operation='AttributeOptimization', default_backend='sat',
52
+ selectable_backend=True,
53
+ inputs=(Input('objectives', Any, required=True),),
54
+ input_adapter=_attribute_optimization_inputs,
55
+ result_adapter=_attribute_optimization_result,
56
+ )
57
+
58
+ @abstractmethod
59
+ def __init__(self) -> None:
60
+ pass
61
+
62
+ @abstractmethod
63
+ def set_attributes(self, attributes: dict[str, OptimizationGoal]) -> None:
64
+ """Set the attributes to optimize as a mapping ``{attribute_name: OptimizationGoal}``."""
65
+
66
+ @abstractmethod
67
+ def optimize(self) -> list[Any]:
68
+ """Return the optimal configuration(s)."""
@@ -0,0 +1,29 @@
1
+ from abc import abstractmethod
2
+
3
+ from flamapy.core.operations import Operation
4
+ from flamapy.core.operations.descriptor import OperationDescriptor
5
+ from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration
6
+
7
+
8
+ class Configurations(Operation):
9
+ facade = OperationDescriptor(
10
+ doc=(
11
+ 'These are the individual outcomes that can be produced from a feature model.\n'
12
+ 'Each product is a combination of features that satisfies all the constraints\n'
13
+ 'and dependencies in the feature model.\n'
14
+ '\n'
15
+ '``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to\n'
16
+ 'bdd.'
17
+ ),
18
+ returns='Union[None, List[Configuration]]',
19
+ name='configurations', operation='Configurations', default_backend='bdd',
20
+ selectable_backend=True,
21
+ )
22
+
23
+ @abstractmethod
24
+ def __init__(self) -> None:
25
+ pass
26
+
27
+ @abstractmethod
28
+ def get_configurations(self) -> list[Configuration]:
29
+ pass
@@ -0,0 +1,30 @@
1
+ from abc import abstractmethod
2
+
3
+ from flamapy.core.operations import Operation
4
+ from flamapy.core.operations.descriptor import OperationDescriptor
5
+
6
+
7
+ class ConfigurationsNumber(Operation):
8
+ facade = OperationDescriptor(
9
+ doc=(
10
+ 'This is the total number of different full configurations that can be produced\n'
11
+ "from a feature model. It's calculated by considering all possible combinations\n"
12
+ 'of features, taking into account the constraints and dependencies between\n'
13
+ 'features.\n'
14
+ '\n'
15
+ '``backend`` selects the analysis plugin ("sat", "bdd", "z3", or "sharpsat" for\n'
16
+ 'a scalable approximate count via the optional flamapy-sharpsat plugin);\n'
17
+ 'defaults to bdd.'
18
+ ),
19
+ returns='Union[None, int]',
20
+ name='configurations_number', operation='ConfigurationsNumber', default_backend='bdd',
21
+ selectable_backend=True,
22
+ )
23
+
24
+ @abstractmethod
25
+ def __init__(self) -> None:
26
+ pass
27
+
28
+ @abstractmethod
29
+ def get_configurations_number(self) -> int:
30
+ pass
@@ -0,0 +1,29 @@
1
+ from abc import abstractmethod
2
+
3
+ from flamapy.core.operations import Operation
4
+ from flamapy.core.operations.descriptor import OperationDescriptor
5
+ from flamapy.core.models.variability_model import VariabilityElement
6
+
7
+
8
+ class CoreFeatures(Operation):
9
+ facade = OperationDescriptor(
10
+ doc=(
11
+ 'These are the features that are present in all products of a product line. In\n'
12
+ 'a feature model, they are the features that are mandatory and not optional.\n'
13
+ 'Core features define the commonality among all products in a product line.\n'
14
+ '\n'
15
+ '``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to\n'
16
+ 'sat.'
17
+ ),
18
+ returns='Union[None, List[str]]',
19
+ name='core_features', operation='CoreFeatures', default_backend='sat',
20
+ selectable_backend=True,
21
+ )
22
+
23
+ @abstractmethod
24
+ def __init__(self) -> None:
25
+ pass
26
+
27
+ @abstractmethod
28
+ def get_core_features(self) -> list[VariabilityElement]:
29
+ pass
@@ -0,0 +1,29 @@
1
+ from abc import abstractmethod
2
+
3
+ from flamapy.core.operations import Operation
4
+ from flamapy.core.operations.descriptor import OperationDescriptor
5
+ from flamapy.core.models.variability_model import VariabilityElement
6
+
7
+
8
+ class DeadFeatures(Operation):
9
+ facade = OperationDescriptor(
10
+ doc=(
11
+ 'These are features that, due to the constraints and dependencies in the\n'
12
+ 'feature model, cannot be included in any valid product. Dead features are\n'
13
+ 'usually a sign of an error in the feature model.\n'
14
+ '\n'
15
+ '``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to\n'
16
+ 'sat.'
17
+ ),
18
+ returns='Union[None, List[str]]',
19
+ name='dead_features', operation='DeadFeatures', default_backend='sat',
20
+ selectable_backend=True,
21
+ )
22
+
23
+ @abstractmethod
24
+ def __init__(self) -> None:
25
+ pass
26
+
27
+ @abstractmethod
28
+ def get_dead_features(self) -> list[VariabilityElement]:
29
+ pass
@@ -0,0 +1,61 @@
1
+ """Self-describing metadata for operations, so the facade/CLI/REST can be *derived* from the
2
+ installed operations instead of hand-written.
3
+
4
+ An operation interface (or a bespoke concrete operation) declares a class attribute ``facade`` of
5
+ type :class:`OperationDescriptor`. It states the operation's stable public name, which backend it
6
+ runs on, its inputs (and how to wire them), and optional input/result adapters for the few
7
+ operations whose facade method is not a plain ``set params -> execute``. Concrete backend
8
+ implementations inherit the descriptor from their interface, so there is exactly one descriptor
9
+ per *logical* operation.
10
+ """
11
+ from dataclasses import dataclass
12
+ from typing import Any, Callable, Iterable, Optional
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class Input:
17
+ """One input of a facade operation and how to feed it to the underlying operation."""
18
+
19
+ name: str
20
+ type: Any = str # a type or typing special form (e.g. Any), for CLI/REST
21
+ default: Any = None
22
+ required: bool = False
23
+ setter: Optional[str] = None # operation setter for the value, e.g. 'set_sample_size'
24
+ kind: str = 'scalar' # 'scalar' | 'configuration' | 'file' (CLI/REST marshalling)
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class OperationDescriptor:
29
+ """Everything the facade/CLI/REST need to expose an operation without hand-written code."""
30
+
31
+ name: str # stable public (facade/CLI/REST) name
32
+ operation: str # name passed to DiscoverMetamodels.use_operation
33
+ kind: str = 'operation' # 'operation' | 'producer' | 'transformer'
34
+ default_backend: Optional[str] = None # None => runs directly on the feature model
35
+ backends: Optional[tuple[str, ...]] = None # allowed backends (None => any implementer)
36
+ selectable_backend: bool = False # facade exposes a backend= kwarg (else fixed)
37
+ inputs: tuple[Input, ...] = ()
38
+ # For the ~12 non-uniform methods: custom wiring / result reshaping. When absent, the generic
39
+ # "call each Input.setter, then execute, then get_result" path is used.
40
+ input_adapter: Optional[Callable[..., Any]] = None
41
+ result_adapter: Optional[Callable[[Any], Any]] = None
42
+ doc: str = ''
43
+ returns: str = 'Any' # return annotation (source text) for the stub
44
+
45
+ @property
46
+ def needs_backend(self) -> bool:
47
+ return self.default_backend is not None
48
+
49
+
50
+ def collect_descriptors(operations: Iterable[type]) -> dict[str, OperationDescriptor]:
51
+ """Collect the ``facade`` descriptors from a set of operation classes, keyed by public name.
52
+
53
+ Concrete backend operations inherit their interface's descriptor, so several classes may yield
54
+ the same descriptor; they are de-duplicated by ``descriptor.name`` (first one wins).
55
+ """
56
+ descriptors: dict[str, OperationDescriptor] = {}
57
+ for operation in operations:
58
+ descriptor = getattr(operation, 'facade', None)
59
+ if isinstance(descriptor, OperationDescriptor):
60
+ descriptors.setdefault(descriptor.name, descriptor)
61
+ return descriptors
@@ -0,0 +1,33 @@
1
+ from abc import abstractmethod
2
+
3
+ from flamapy.core.operations import Operation
4
+ from flamapy.core.operations.descriptor import OperationDescriptor
5
+ from flamapy.core.models.variability_model import VariabilityElement
6
+
7
+
8
+ class FalseOptionalFeatures(Operation):
9
+ """A feature is defined as `false optional` if the selection of its parent makes the feature
10
+ itself selected as well, although it is defined as optional and not mandatory.
11
+
12
+ A feature in a group can be also `false optional`.
13
+ In general, false optional features should be marked as mandatory for maintainability purposes.
14
+ """
15
+
16
+ facade = OperationDescriptor(
17
+ doc=(
18
+ 'These are features that appear to be optional in the feature model, but due to\n'
19
+ 'the constraints and dependencies, must be included in every valid product.\n'
20
+ 'Like dead features, false optional features are usually a sign of an error in\n'
21
+ 'the feature model.\n'
22
+ '\n'
23
+ '``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to\n'
24
+ 'sat.'
25
+ ),
26
+ returns='Union[None, List[str]]',
27
+ name='false_optional_features', operation='FalseOptionalFeatures', default_backend='sat',
28
+ selectable_backend=True,
29
+ )
30
+
31
+ @abstractmethod
32
+ def get_false_optional_features(self) -> list[VariabilityElement]:
33
+ pass
@@ -2,6 +2,7 @@ from abc import abstractmethod
2
2
 
3
3
  from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration
4
4
  from flamapy.core.operations import Operation
5
+ from flamapy.core.operations.descriptor import OperationDescriptor, Input
5
6
 
6
7
 
7
8
  class Sampling(Operation):
@@ -9,6 +10,24 @@ class Sampling(Operation):
9
10
  (or configurations) from within a variability model.
10
11
  """
11
12
 
13
+ facade = OperationDescriptor(
14
+ doc=(
15
+ 'Returns a random sample of valid configurations of the given size. When\n'
16
+ 'with_replacement is True, the same configuration may appear more than once.\n'
17
+ '\n'
18
+ '``backend`` selects the analysis plugin; defaults to bdd. sat and bdd\n'
19
+ 'implement (deterministic/enumerating) sampling; "sharpsat" provides almost-\n'
20
+ 'uniform sampling via the optional flamapy-sharpsat plugin.'
21
+ ),
22
+ returns='Union[None, List[Configuration]]',
23
+ name='sampling', operation='Sampling', default_backend='bdd',
24
+ selectable_backend=True,
25
+ inputs=(
26
+ Input('size', int, required=True, setter='set_sample_size'),
27
+ Input('with_replacement', bool, default=False, setter='set_with_replacement'),
28
+ ),
29
+ )
30
+
12
31
  @abstractmethod
13
32
  def __init__(self) -> None:
14
33
  pass
@@ -0,0 +1,28 @@
1
+ from abc import abstractmethod
2
+
3
+ from flamapy.core.operations import Operation
4
+ from flamapy.core.operations.descriptor import OperationDescriptor
5
+
6
+
7
+ class Satisfiable(Operation):
8
+ facade = OperationDescriptor(
9
+ doc=(
10
+ 'In the context of feature models, this usually refers to whether the feature\n'
11
+ 'model itself satisfies all the constraints and dependencies. A a valid feature\n'
12
+ 'model is one that does encodes at least a single valid product.\n'
13
+ '\n'
14
+ '``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to\n'
15
+ 'sat.'
16
+ ),
17
+ returns='Union[None, bool]',
18
+ name='satisfiable', operation='Satisfiable', default_backend='sat',
19
+ selectable_backend=True,
20
+ )
21
+
22
+ @abstractmethod
23
+ def __init__(self) -> None:
24
+ pass
25
+
26
+ @abstractmethod
27
+ def is_satisfiable(self) -> bool:
28
+ pass
@@ -0,0 +1,47 @@
1
+ from abc import abstractmethod
2
+ from typing import Any
3
+
4
+ from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration
5
+ from flamapy.core.operations import Operation
6
+ from flamapy.core.operations.descriptor import OperationDescriptor, Input
7
+
8
+
9
+ def _satisfiable_configuration_inputs(operation: Any, facade: Any, kwargs: dict[str, Any]) -> None:
10
+ configuration = facade._as_configuration(kwargs['configuration_path'])
11
+ configuration.is_full = bool(kwargs.get('full_configuration', False))
12
+ operation.set_configuration(configuration)
13
+
14
+
15
+ class SatisfiableConfiguration(Operation):
16
+ facade = OperationDescriptor(
17
+ doc=(
18
+ 'This is a product that is produced from a valid configuration of features. A\n'
19
+ 'valid product satisfies all the constraints and dependencies in the feature\n'
20
+ 'model.\n'
21
+ '\n'
22
+ '``configuration_path`` accepts a configuration file path, a ``{feature:\n'
23
+ 'value}`` mapping, or a Configuration object. ``backend`` selects the analysis\n'
24
+ 'plugin ("sat", "bdd" or "z3"); defaults to sat.'
25
+ ),
26
+ returns='Union[None, bool]',
27
+ name='satisfiable_configuration', operation='SatisfiableConfiguration',
28
+ default_backend='sat', selectable_backend=True,
29
+ inputs=(
30
+ Input('configuration_path', str, required=True, kind='configuration',
31
+ setter='set_configuration'),
32
+ Input('full_configuration', bool, default=False),
33
+ ),
34
+ input_adapter=_satisfiable_configuration_inputs,
35
+ )
36
+
37
+ @abstractmethod
38
+ def __init__(self) -> None:
39
+ pass
40
+
41
+ @abstractmethod
42
+ def set_configuration(self, configuration: Configuration) -> None:
43
+ pass
44
+
45
+ @abstractmethod
46
+ def is_satisfiable(self) -> bool:
47
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flamapy-fw
3
- Version: 2.6.0.dev5
3
+ Version: 2.6.0.dev11
4
4
  Summary: Flamapy is a Python-based AAFM framework that takes into consideration previous AAFM tool designs and enables multi-solver and multi-metamodel support for the integration of AAFM tooling on the Python ecosystem.
5
5
  Author-email: Flamapy <flamapy@us.es>
6
6
  License-Expression: GPL-3.0-or-later
@@ -22,6 +22,7 @@ flamapy/core/operations/configurations_number.py
22
22
  flamapy/core/operations/core_features.py
23
23
  flamapy/core/operations/count_leafs.py
24
24
  flamapy/core/operations/dead_features.py
25
+ flamapy/core/operations/descriptor.py
25
26
  flamapy/core/operations/error_detection.py
26
27
  flamapy/core/operations/error_diagnosis.py
27
28
  flamapy/core/operations/estimated_configurations_number.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "flamapy-fw"
7
- version = "2.6.0.dev5"
7
+ version = "2.6.0.dev11"
8
8
  description = "Flamapy is a Python-based AAFM framework that takes into consideration previous AAFM tool designs and enables multi-solver and multi-metamodel support for the integration of AAFM tooling on the Python ecosystem."
9
9
  readme = "README.md"
10
10
  license = "GPL-3.0-or-later"
@@ -1,30 +0,0 @@
1
- from abc import abstractmethod
2
- from enum import Enum
3
- from typing import Any
4
-
5
- from flamapy.core.operations import Operation
6
-
7
-
8
- class OptimizationGoal(Enum):
9
- MAXIMIZE = 'Maximize'
10
- MINIMIZE = 'Minimize'
11
-
12
-
13
- class AttributeOptimization(Operation):
14
- """Return the configuration(s) that optimize the given numeric feature attribute(s).
15
-
16
- Attributes are identified by name and mapped to an :class:`OptimizationGoal`. The
17
- objective for each attribute is the sum of its values over the selected features.
18
- """
19
-
20
- @abstractmethod
21
- def __init__(self) -> None:
22
- pass
23
-
24
- @abstractmethod
25
- def set_attributes(self, attributes: dict[str, OptimizationGoal]) -> None:
26
- """Set the attributes to optimize as a mapping ``{attribute_name: OptimizationGoal}``."""
27
-
28
- @abstractmethod
29
- def optimize(self) -> list[Any]:
30
- """Return the optimal configuration(s)."""
@@ -1,14 +0,0 @@
1
- from abc import abstractmethod
2
-
3
- from flamapy.core.operations import Operation
4
- from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration
5
-
6
-
7
- class Configurations(Operation):
8
- @abstractmethod
9
- def __init__(self) -> None:
10
- pass
11
-
12
- @abstractmethod
13
- def get_configurations(self) -> list[Configuration]:
14
- pass
@@ -1,13 +0,0 @@
1
- from abc import abstractmethod
2
-
3
- from flamapy.core.operations import Operation
4
-
5
-
6
- class ConfigurationsNumber(Operation):
7
- @abstractmethod
8
- def __init__(self) -> None:
9
- pass
10
-
11
- @abstractmethod
12
- def get_configurations_number(self) -> int:
13
- pass
@@ -1,14 +0,0 @@
1
- from abc import abstractmethod
2
-
3
- from flamapy.core.operations import Operation
4
- from flamapy.core.models.variability_model import VariabilityElement
5
-
6
-
7
- class CoreFeatures(Operation):
8
- @abstractmethod
9
- def __init__(self) -> None:
10
- pass
11
-
12
- @abstractmethod
13
- def get_core_features(self) -> list[VariabilityElement]:
14
- pass
@@ -1,14 +0,0 @@
1
- from abc import abstractmethod
2
-
3
- from flamapy.core.operations import Operation
4
- from flamapy.core.models.variability_model import VariabilityElement
5
-
6
-
7
- class DeadFeatures(Operation):
8
- @abstractmethod
9
- def __init__(self) -> None:
10
- pass
11
-
12
- @abstractmethod
13
- def get_dead_features(self) -> list[VariabilityElement]:
14
- pass
@@ -1,17 +0,0 @@
1
- from abc import abstractmethod
2
-
3
- from flamapy.core.operations import Operation
4
- from flamapy.core.models.variability_model import VariabilityElement
5
-
6
-
7
- class FalseOptionalFeatures(Operation):
8
- """A feature is defined as `false optional` if the selection of its parent makes the feature
9
- itself selected as well, although it is defined as optional and not mandatory.
10
-
11
- A feature in a group can be also `false optional`.
12
- In general, false optional features should be marked as mandatory for maintainability purposes.
13
- """
14
-
15
- @abstractmethod
16
- def get_false_optional_features(self) -> list[VariabilityElement]:
17
- pass
@@ -1,13 +0,0 @@
1
- from abc import abstractmethod
2
-
3
- from flamapy.core.operations import Operation
4
-
5
-
6
- class Satisfiable(Operation):
7
- @abstractmethod
8
- def __init__(self) -> None:
9
- pass
10
-
11
- @abstractmethod
12
- def is_satisfiable(self) -> bool:
13
- pass
@@ -1,18 +0,0 @@
1
- from abc import abstractmethod
2
-
3
- from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration
4
- from flamapy.core.operations import Operation
5
-
6
-
7
- class SatisfiableConfiguration(Operation):
8
- @abstractmethod
9
- def __init__(self) -> None:
10
- pass
11
-
12
- @abstractmethod
13
- def set_configuration(self, configuration: Configuration) -> None:
14
- pass
15
-
16
- @abstractmethod
17
- def is_satisfiable(self) -> bool:
18
- pass