flamapy-fw 2.6.0.dev5__tar.gz → 2.6.0.dev12__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_fw-2.6.0.dev5/flamapy_fw.egg-info → flamapy_fw-2.6.0.dev12}/PKG-INFO +1 -1
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/discover.py +8 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/__init__.py +4 -0
- flamapy_fw-2.6.0.dev12/flamapy/core/operations/attribute_optimization.py +68 -0
- flamapy_fw-2.6.0.dev12/flamapy/core/operations/configurations.py +29 -0
- flamapy_fw-2.6.0.dev12/flamapy/core/operations/configurations_number.py +30 -0
- flamapy_fw-2.6.0.dev12/flamapy/core/operations/core_features.py +29 -0
- flamapy_fw-2.6.0.dev12/flamapy/core/operations/dead_features.py +29 -0
- flamapy_fw-2.6.0.dev12/flamapy/core/operations/descriptor.py +61 -0
- flamapy_fw-2.6.0.dev12/flamapy/core/operations/false_optional_features.py +33 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/sampling.py +19 -0
- flamapy_fw-2.6.0.dev12/flamapy/core/operations/satisfiable.py +28 -0
- flamapy_fw-2.6.0.dev12/flamapy/core/operations/satisfiable_configuration.py +47 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12/flamapy_fw.egg-info}/PKG-INFO +1 -1
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy_fw.egg-info/SOURCES.txt +1 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/pyproject.toml +1 -1
- flamapy_fw-2.6.0.dev5/flamapy/core/operations/attribute_optimization.py +0 -30
- flamapy_fw-2.6.0.dev5/flamapy/core/operations/configurations.py +0 -14
- flamapy_fw-2.6.0.dev5/flamapy/core/operations/configurations_number.py +0 -13
- flamapy_fw-2.6.0.dev5/flamapy/core/operations/core_features.py +0 -14
- flamapy_fw-2.6.0.dev5/flamapy/core/operations/dead_features.py +0 -14
- flamapy_fw-2.6.0.dev5/flamapy/core/operations/false_optional_features.py +0 -17
- flamapy_fw-2.6.0.dev5/flamapy/core/operations/satisfiable.py +0 -13
- flamapy_fw-2.6.0.dev5/flamapy/core/operations/satisfiable_configuration.py +0 -18
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/LICENSE +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/README.md +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/__init__.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/config.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/exceptions.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/models/__init__.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/models/ast.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/models/variability_model.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/abstract_operation.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/atomic_sets.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/average_branching_factor.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/commonality.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/count_leafs.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/error_detection.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/error_diagnosis.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/estimated_configurations_number.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/filter.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/metrics_operation.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/variability.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/plugins.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/transformations/__init__.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/transformations/abstract_transformation.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/transformations/model_to_model.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/transformations/model_to_text.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/transformations/text_to_model.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/utils.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/metamodels/configuration_metamodel/__init__.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/metamodels/configuration_metamodel/models/__init__.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/metamodels/configuration_metamodel/models/configuration.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/metamodels/configuration_metamodel/transformations/__init__.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/metamodels/configuration_metamodel/transformations/configuration_basic_reader.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/metamodels/configuration_metamodel/transformations/configuration_basic_writer.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/metamodels/configuration_metamodel/transformations/configuration_json_reader.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/metamodels/configuration_metamodel/transformations/configuration_json_writer.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/metamodels/configuration_metamodel/transformations/uvls_json_reader.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/metamodels/configuration_metamodel/transformations/uvls_json_writer.py +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy_fw.egg-info/dependency_links.txt +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy_fw.egg-info/entry_points.txt +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy_fw.egg-info/requires.txt +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy_fw.egg-info/top_level.txt +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/setup.cfg +0 -0
- {flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flamapy-fw
|
|
3
|
-
Version: 2.6.0.
|
|
3
|
+
Version: 2.6.0.dev12
|
|
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.
|
|
3
|
+
Version: 2.6.0.dev12
|
|
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.
|
|
7
|
+
version = "2.6.0.dev12"
|
|
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,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
|
|
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_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/abstract_operation.py
RENAMED
|
File without changes
|
|
File without changes
|
{flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/average_branching_factor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/operations/metrics_operation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/transformations/model_to_model.py
RENAMED
|
File without changes
|
{flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/transformations/model_to_text.py
RENAMED
|
File without changes
|
{flamapy_fw-2.6.0.dev5 → flamapy_fw-2.6.0.dev12}/flamapy/core/transformations/text_to_model.py
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|