flamapy-z3 2.5.0.dev0__tar.gz → 2.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flamapy_z3-2.6.0/PKG-INFO +32 -0
- flamapy_z3-2.6.0/README.md +13 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/models/z3_model.py +3 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/__init__.py +3 -0
- flamapy_z3-2.6.0/flamapy/metamodels/z3_metamodel/operations/interfaces/attribute_optimization.py +12 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/z3_all_feature_bounds.py +11 -0
- flamapy_z3-2.6.0/flamapy/metamodels/z3_metamodel/operations/z3_backbone.py +70 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/z3_core_features.py +2 -16
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/z3_dead_features.py +2 -16
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/z3_false_optional_features.py +6 -6
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/z3_feature_bounds.py +12 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/transformations/fm_to_z3.py +58 -7
- flamapy_z3-2.6.0/flamapy_z3.egg-info/PKG-INFO +32 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy_z3.egg-info/SOURCES.txt +2 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy_z3.egg-info/requires.txt +2 -2
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/pyproject.toml +5 -5
- flamapy_z3-2.6.0/tests/test_tseytin.py +69 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/tests/test_z3_metamodel.py +2 -1
- flamapy_z3-2.5.0.dev0/PKG-INFO +0 -202
- flamapy_z3-2.5.0.dev0/README.md +0 -183
- flamapy_z3-2.5.0.dev0/flamapy/metamodels/z3_metamodel/operations/interfaces/attribute_optimization.py +0 -27
- flamapy_z3-2.5.0.dev0/flamapy_z3.egg-info/PKG-INFO +0 -202
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/__init__.py +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/models/__init__.py +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/interfaces/__init__.py +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/z3_attribute_optimization.py +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/z3_configurations.py +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/z3_configurations_number.py +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/z3_satisfiable.py +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/z3_satisfiable_configuration.py +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/z3_variable_bounds_complex.py +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/transformations/__init__.py +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy_z3.egg-info/dependency_links.txt +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy_z3.egg-info/top_level.txt +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/setup.cfg +0 -0
- {flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/setup.py +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flamapy-z3
|
|
3
|
+
Version: 2.6.0
|
|
4
|
+
Summary: z3-plugin for the automated analysis of feature models
|
|
5
|
+
Author-email: Flamapy <flamapy@us.es>
|
|
6
|
+
License-Expression: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/flamapy/z3_metamodel
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: flamapy-fw~=2.6.0
|
|
11
|
+
Requires-Dist: flamapy-fm~=2.6.0
|
|
12
|
+
Requires-Dist: z3-solver~=4.14.1.0
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
16
|
+
Requires-Dist: prospector; extra == "dev"
|
|
17
|
+
Requires-Dist: mypy; extra == "dev"
|
|
18
|
+
Requires-Dist: coverage; extra == "dev"
|
|
19
|
+
|
|
20
|
+
# flamapy-z3
|
|
21
|
+
|
|
22
|
+
SMT-based analysis for [flamapy](https://flamapy.org) feature models with typed
|
|
23
|
+
attributes (Integer, Real, String), built on the [Z3](https://github.com/Z3Prover/z3)
|
|
24
|
+
solver.
|
|
25
|
+
|
|
26
|
+
**Documentation:** https://docs.flamapy.org/framework/plugins/z3_plugin
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install flamapy-z3
|
|
32
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# flamapy-z3
|
|
2
|
+
|
|
3
|
+
SMT-based analysis for [flamapy](https://flamapy.org) feature models with typed
|
|
4
|
+
attributes (Integer, Real, String), built on the [Z3](https://github.com/Z3Prover/z3)
|
|
5
|
+
solver.
|
|
6
|
+
|
|
7
|
+
**Documentation:** https://docs.flamapy.org/framework/plugins/z3_plugin
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install flamapy-z3
|
|
13
|
+
```
|
{flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/models/z3_model.py
RENAMED
|
@@ -35,6 +35,9 @@ class Z3Model(VariabilityModel):
|
|
|
35
35
|
self.attributes: dict[str, list[Any]] = {} # attr_name -> [z3var]
|
|
36
36
|
self.attributes_types: dict[str, AttributeType] = {} # attr_name -> AttributeType
|
|
37
37
|
self.constraints: list[Any] = [] # list of z3 expressions
|
|
38
|
+
# Auxiliary (Tseytin) boolean variables. Kept out of ``features`` so that feature
|
|
39
|
+
# enumeration/counting/bounds operations ignore them.
|
|
40
|
+
self.auxiliary_variables: list[Any] = []
|
|
38
41
|
self.original_model: Optional[VariabilityModel] = None
|
|
39
42
|
|
|
40
43
|
def create_const(self, ftype: FeatureType | AttributeType, value: Any) -> Any:
|
{flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/operations/__init__.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from .z3_satisfiable import Z3Satisfiable
|
|
2
2
|
from .z3_configurations import Z3Configurations
|
|
3
3
|
from .z3_configurations_number import Z3ConfigurationsNumber
|
|
4
|
+
from .z3_backbone import Z3Backbone
|
|
4
5
|
from .z3_core_features import Z3CoreFeatures
|
|
5
6
|
from .z3_dead_features import Z3DeadFeatures
|
|
6
7
|
from .z3_false_optional_features import Z3FalseOptionalFeatures
|
|
@@ -9,9 +10,11 @@ from .z3_satisfiable_configuration import Z3SatisfiableConfiguration
|
|
|
9
10
|
from .z3_feature_bounds import Z3FeatureBounds
|
|
10
11
|
from .z3_all_feature_bounds import Z3AllFeatureBounds
|
|
11
12
|
|
|
13
|
+
|
|
12
14
|
__all__ = [
|
|
13
15
|
'Z3AllFeatureBounds',
|
|
14
16
|
'Z3AttributeOptimization',
|
|
17
|
+
'Z3Backbone',
|
|
15
18
|
'Z3Configurations',
|
|
16
19
|
'Z3ConfigurationsNumber',
|
|
17
20
|
'Z3CoreFeatures',
|
flamapy_z3-2.6.0/flamapy/metamodels/z3_metamodel/operations/interfaces/attribute_optimization.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Backward-compatible re-export.
|
|
2
|
+
|
|
3
|
+
``AttributeOptimization`` and ``OptimizationGoal`` now live in the core framework so
|
|
4
|
+
that every backend shares one interface. This module keeps the historical import path
|
|
5
|
+
``flamapy.metamodels.z3_metamodel.operations.interfaces.attribute_optimization`` working.
|
|
6
|
+
"""
|
|
7
|
+
from flamapy.core.operations.attribute_optimization import (
|
|
8
|
+
AttributeOptimization,
|
|
9
|
+
OptimizationGoal,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = ["AttributeOptimization", "OptimizationGoal"]
|
|
@@ -3,6 +3,7 @@ from typing import Any, cast
|
|
|
3
3
|
|
|
4
4
|
from flamapy.core.models import VariabilityModel
|
|
5
5
|
from flamapy.core.operations import Operation
|
|
6
|
+
from flamapy.core.operations.descriptor import OperationDescriptor
|
|
6
7
|
from flamapy.metamodels.z3_metamodel.models import Z3Model
|
|
7
8
|
from flamapy.metamodels.z3_metamodel.operations import Z3FeatureBounds
|
|
8
9
|
from flamapy.metamodels.fm_metamodel.models import FeatureType
|
|
@@ -16,6 +17,16 @@ class Z3AllFeatureBounds(Operation):
|
|
|
16
17
|
(Integer, Real, String length) in the Z3 model.
|
|
17
18
|
"""
|
|
18
19
|
|
|
20
|
+
facade = OperationDescriptor(
|
|
21
|
+
doc=(
|
|
22
|
+
'Returns the value bounds for all typed (Integer, Real, String) attribute\n'
|
|
23
|
+
"features in the model. Each entry maps a feature name to a dict with 'min',\n"
|
|
24
|
+
"'max', and 'bounded' keys. Requires the z3 metamodel plugin."
|
|
25
|
+
),
|
|
26
|
+
returns='Union[None, Dict[str, Dict[str, Any]]]',
|
|
27
|
+
name='all_feature_bounds', operation='Z3AllFeatureBounds', default_backend='z3'
|
|
28
|
+
)
|
|
29
|
+
|
|
19
30
|
def __init__(self) -> None:
|
|
20
31
|
# El resultado será un diccionario donde la clave es el nombre de la variable
|
|
21
32
|
# y el valor es el diccionario de bounds (min, max, bounded).
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from typing import Any, cast
|
|
2
|
+
|
|
3
|
+
import z3
|
|
4
|
+
|
|
5
|
+
from flamapy.core.models import VariabilityModel
|
|
6
|
+
from flamapy.core.operations import Operation
|
|
7
|
+
from flamapy.metamodels.z3_metamodel.models import Z3Model
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Z3Backbone(Operation):
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
self._result: dict[str, list[Any]] = {"core": [], "dead": []}
|
|
14
|
+
|
|
15
|
+
def get_backbone(self) -> dict[str, list[Any]]:
|
|
16
|
+
return self.get_result()
|
|
17
|
+
|
|
18
|
+
def get_result(self) -> dict[str, list[Any]]:
|
|
19
|
+
return self._result
|
|
20
|
+
|
|
21
|
+
def execute(self, model: VariabilityModel) -> 'Z3Backbone':
|
|
22
|
+
z3_model = cast(Z3Model, model)
|
|
23
|
+
self._result = get_backbone(z3_model)
|
|
24
|
+
return self
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_backbone(model: Z3Model) -> dict[str, list[Any]]:
|
|
28
|
+
solver = z3.Solver(ctx=model.ctx)
|
|
29
|
+
solver.add(model.constraints)
|
|
30
|
+
|
|
31
|
+
vars_to_features = {f_info.sel: name for name, f_info in model.features.items()}
|
|
32
|
+
|
|
33
|
+
# (out, C) <- SAT(phi)
|
|
34
|
+
if solver.check() != z3.sat:
|
|
35
|
+
return {"core": [], "dead": list(model.features.keys())}
|
|
36
|
+
|
|
37
|
+
m = solver.model()
|
|
38
|
+
# C <- filter(C)
|
|
39
|
+
core_candidates = {var for var in vars_to_features.keys() if z3.is_true(m[var])}
|
|
40
|
+
dead_candidates = {var for var in vars_to_features.keys() if z3.is_false(m[var])}
|
|
41
|
+
|
|
42
|
+
backbone_core = []
|
|
43
|
+
backbone_dead = []
|
|
44
|
+
|
|
45
|
+
while core_candidates or dead_candidates:
|
|
46
|
+
if core_candidates:
|
|
47
|
+
literal = next(iter(core_candidates))
|
|
48
|
+
check_lit = z3.Not(literal)
|
|
49
|
+
is_core = True
|
|
50
|
+
else:
|
|
51
|
+
literal = next(iter(dead_candidates))
|
|
52
|
+
check_lit = literal
|
|
53
|
+
is_core = False
|
|
54
|
+
|
|
55
|
+
# (out, S) <- SAT(phi U {not l})
|
|
56
|
+
if solver.check([check_lit]) == z3.unsat:
|
|
57
|
+
if is_core:
|
|
58
|
+
backbone_core.append(vars_to_features[literal])
|
|
59
|
+
solver.add(literal)
|
|
60
|
+
core_candidates.remove(literal)
|
|
61
|
+
else:
|
|
62
|
+
backbone_dead.append(vars_to_features[literal])
|
|
63
|
+
solver.add(z3.Not(literal))
|
|
64
|
+
dead_candidates.remove(literal)
|
|
65
|
+
else:
|
|
66
|
+
new_model = solver.model()
|
|
67
|
+
core_candidates = {var for var in core_candidates if z3.is_true(new_model[var])}
|
|
68
|
+
dead_candidates = {var for var in dead_candidates if z3.is_false(new_model[var])}
|
|
69
|
+
|
|
70
|
+
return {"core": backbone_core, "dead": backbone_dead}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
from typing import Any, cast
|
|
2
2
|
|
|
3
|
-
import z3
|
|
4
|
-
|
|
5
3
|
from flamapy.core.models import VariabilityModel
|
|
6
4
|
from flamapy.core.operations import CoreFeatures
|
|
7
5
|
from flamapy.metamodels.z3_metamodel.models import Z3Model
|
|
6
|
+
from flamapy.metamodels.z3_metamodel.operations import Z3Backbone
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
class Z3CoreFeatures(CoreFeatures):
|
|
@@ -20,18 +19,5 @@ class Z3CoreFeatures(CoreFeatures):
|
|
|
20
19
|
|
|
21
20
|
def execute(self, model: VariabilityModel) -> 'Z3CoreFeatures':
|
|
22
21
|
z3_model = cast(Z3Model, model)
|
|
23
|
-
self._result =
|
|
22
|
+
self._result = Z3Backbone().execute(z3_model).get_result()["core"]
|
|
24
23
|
return self
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def get_core_features(model: Z3Model) -> list[Any]:
|
|
28
|
-
solver = z3.Solver(ctx=model.ctx)
|
|
29
|
-
solver.add(model.constraints)
|
|
30
|
-
|
|
31
|
-
core_features = []
|
|
32
|
-
if solver.check() == z3.sat:
|
|
33
|
-
for feature, feature_info in model.features.items():
|
|
34
|
-
variable = feature_info.sel
|
|
35
|
-
if solver.check([z3.Not(variable)]) == z3.unsat:
|
|
36
|
-
core_features.append(feature)
|
|
37
|
-
return core_features
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
from typing import Any, cast
|
|
2
2
|
|
|
3
|
-
import z3
|
|
4
|
-
|
|
5
3
|
from flamapy.core.models import VariabilityModel
|
|
6
4
|
from flamapy.core.operations import DeadFeatures
|
|
7
5
|
from flamapy.metamodels.z3_metamodel.models import Z3Model
|
|
6
|
+
from flamapy.metamodels.z3_metamodel.operations import Z3Backbone
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
class Z3DeadFeatures(DeadFeatures):
|
|
@@ -20,18 +19,5 @@ class Z3DeadFeatures(DeadFeatures):
|
|
|
20
19
|
|
|
21
20
|
def execute(self, model: VariabilityModel) -> 'Z3DeadFeatures':
|
|
22
21
|
z3_model = cast(Z3Model, model)
|
|
23
|
-
self._result =
|
|
22
|
+
self._result = Z3Backbone().execute(z3_model).get_result()["dead"]
|
|
24
23
|
return self
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def get_dead_features(model: Z3Model) -> list[Any]:
|
|
28
|
-
solver = z3.Solver(ctx=model.ctx)
|
|
29
|
-
solver.add(model.constraints)
|
|
30
|
-
|
|
31
|
-
dead_features = []
|
|
32
|
-
if solver.check() == z3.sat:
|
|
33
|
-
for feature, feature_info in model.features.items():
|
|
34
|
-
variable = feature_info.sel
|
|
35
|
-
if solver.check([variable]) == z3.unsat:
|
|
36
|
-
dead_features.append(feature)
|
|
37
|
-
return dead_features
|
|
@@ -10,7 +10,7 @@ from flamapy.metamodels.fm_metamodel.models import FeatureModel
|
|
|
10
10
|
from flamapy.metamodels.z3_metamodel.models import Z3Model
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
LOGGER = logging.getLogger('
|
|
13
|
+
LOGGER = logging.getLogger('Z3FalseOptionalFeatures')
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class Z3FalseOptionalFeatures(FalseOptionalFeatures):
|
|
@@ -26,11 +26,11 @@ class Z3FalseOptionalFeatures(FalseOptionalFeatures):
|
|
|
26
26
|
|
|
27
27
|
def execute(self, model: VariabilityModel) -> 'Z3FalseOptionalFeatures':
|
|
28
28
|
z3_model = cast(Z3Model, model)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
original_model = getattr(model, 'original_model', None)
|
|
30
|
+
if original_model is None:
|
|
31
|
+
raise FlamaException("The transformation didn't attach the source model, "
|
|
32
|
+
"which is required for this operation.")
|
|
33
|
+
feature_model = cast(FeatureModel, original_model)
|
|
34
34
|
self._result = get_false_optional_features(z3_model, feature_model)
|
|
35
35
|
return self
|
|
36
36
|
|
|
@@ -5,6 +5,7 @@ import z3
|
|
|
5
5
|
|
|
6
6
|
from flamapy.core.models import VariabilityModel
|
|
7
7
|
from flamapy.core.operations import Operation
|
|
8
|
+
from flamapy.core.operations.descriptor import OperationDescriptor, Input
|
|
8
9
|
from flamapy.metamodels.z3_metamodel.models import Z3Model
|
|
9
10
|
from flamapy.metamodels.fm_metamodel.models import FeatureType
|
|
10
11
|
|
|
@@ -20,6 +21,17 @@ class Z3FeatureBounds(Operation):
|
|
|
20
21
|
and the length bounds (min_len/max_len) for string features, with safety checks.
|
|
21
22
|
"""
|
|
22
23
|
|
|
24
|
+
facade = OperationDescriptor(
|
|
25
|
+
doc=(
|
|
26
|
+
'Returns the value bounds for a specific typed attribute feature. The result\n'
|
|
27
|
+
"dict contains 'min', 'max', and 'bounded' keys. Requires the z3 metamodel\n"
|
|
28
|
+
'plugin.'
|
|
29
|
+
),
|
|
30
|
+
returns='Union[None, Dict[str, Any]]',
|
|
31
|
+
name='feature_bounds', operation='Z3FeatureBounds', default_backend='z3',
|
|
32
|
+
inputs=(Input('variable_name', str, required=True, setter='set_variable_name'),),
|
|
33
|
+
)
|
|
34
|
+
|
|
23
35
|
def __init__(self) -> None:
|
|
24
36
|
self._result: dict[str, Any] = {}
|
|
25
37
|
self._variable_name: Optional[str] = None
|
|
@@ -61,10 +61,16 @@ class FmToZ3(ModelToModel):
|
|
|
61
61
|
def get_destination_extension() -> str:
|
|
62
62
|
return "z3"
|
|
63
63
|
|
|
64
|
-
def __init__(self, source_model: FeatureModel) -> None:
|
|
64
|
+
def __init__(self, source_model: FeatureModel, cnf_method: str = 'direct') -> None:
|
|
65
65
|
self.source_model = source_model
|
|
66
66
|
self.destination_model: Z3Model = Z3Model()
|
|
67
67
|
self._counter: int = 0
|
|
68
|
+
# 'direct' (default) builds Z3 boolean expressions straight from the AST. 'tseytin'
|
|
69
|
+
# applies the Tseytin CNF encoding to purely propositional boolean constraints
|
|
70
|
+
# (others fall back to 'direct'). Framework-only opt-in; Z3 already clausifies
|
|
71
|
+
# internally, so this exists for cross-backend uniformity/experimentation, not speed.
|
|
72
|
+
self.cnf_method = cnf_method
|
|
73
|
+
self._aux_counter: int = 0
|
|
68
74
|
|
|
69
75
|
def transform(self) -> Z3Model:
|
|
70
76
|
self.destination_model = Z3Model()
|
|
@@ -195,7 +201,6 @@ class FmToZ3(ModelToModel):
|
|
|
195
201
|
self.destination_model.add_constraint(formula)
|
|
196
202
|
|
|
197
203
|
def _add_mutex_formula(self, relation: Relation) -> None:
|
|
198
|
-
formulas = []
|
|
199
204
|
parent_variable = self.destination_model.get_variable(relation.parent.name)
|
|
200
205
|
if parent_variable is None:
|
|
201
206
|
raise FlamaException(f'Unsupported feature: {relation.parent.name}')
|
|
@@ -206,12 +211,13 @@ class FmToZ3(ModelToModel):
|
|
|
206
211
|
if child_variable is None:
|
|
207
212
|
raise FlamaException(f'Unsupported feature: {child.name}')
|
|
208
213
|
children.add(child_variable.sel)
|
|
214
|
+
one_child_formula = []
|
|
209
215
|
for child in children:
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
formula = z3.Or(
|
|
216
|
+
other_children_or = z3.Or(*list(children - {child}))
|
|
217
|
+
one_child_formula.append(z3.Not(child) == other_children_or)
|
|
218
|
+
one_child_formula = z3.And(*one_child_formula)
|
|
219
|
+
no_child_formula = z3.And(*[z3.Not(ch) for ch in children])
|
|
220
|
+
formula = z3.Or(no_child_formula, z3.And(one_child_formula, parent))
|
|
215
221
|
self.destination_model.add_constraint(formula)
|
|
216
222
|
|
|
217
223
|
def _build_literals(self, children: list[Any], combination: tuple[Any, ...]) -> list[Any]:
|
|
@@ -258,9 +264,54 @@ class FmToZ3(ModelToModel):
|
|
|
258
264
|
self._add_inactive_parent_constraints(parent, children)
|
|
259
265
|
|
|
260
266
|
def _add_constraint_formula(self, ctc: Constraint) -> None:
|
|
267
|
+
if self.cnf_method == 'tseytin' and self._is_propositional_boolean(ctc):
|
|
268
|
+
self._add_tseytin_constraint(ctc)
|
|
269
|
+
return
|
|
261
270
|
expr = self._get_expression(ctc.ast.root, None)
|
|
262
271
|
self.destination_model.add_constraint(expr)
|
|
263
272
|
|
|
273
|
+
def _is_propositional_boolean(self, ctc: Constraint) -> bool:
|
|
274
|
+
"""Whether a constraint is purely propositional over boolean features, i.e. safe
|
|
275
|
+
for the Tseytin CNF encoding. Arithmetic/aggregation/typed constraints are not."""
|
|
276
|
+
if any(op not in LOGICAL_OPERATORS for op in ctc.ast.get_operators()):
|
|
277
|
+
return False
|
|
278
|
+
for operand in ctc.ast.get_operands():
|
|
279
|
+
if not isinstance(operand, str):
|
|
280
|
+
return False
|
|
281
|
+
info = self.destination_model.get_variable(operand)
|
|
282
|
+
if info is None or info.ftype != FeatureType.BOOLEAN:
|
|
283
|
+
return False
|
|
284
|
+
return True
|
|
285
|
+
|
|
286
|
+
def _add_tseytin_constraint(self, ctc: Constraint) -> None:
|
|
287
|
+
clauses, aux_names = ctc.ast.get_clauses_with_aux(method='tseytin')
|
|
288
|
+
ctx = self.destination_model.ctx
|
|
289
|
+
# Fresh, globally-unique Z3 Bool per auxiliary name (same-named Bools in one Z3
|
|
290
|
+
# context are the same variable, so per-constraint names must not collide).
|
|
291
|
+
local: dict[str, Any] = {}
|
|
292
|
+
for name in aux_names:
|
|
293
|
+
self._aux_counter += 1
|
|
294
|
+
aux_var = z3.Bool(f'__tseytin_aux_{self._aux_counter}', ctx=ctx)
|
|
295
|
+
local[name] = aux_var
|
|
296
|
+
self.destination_model.auxiliary_variables.append(aux_var)
|
|
297
|
+
|
|
298
|
+
def literal_expr(term: str) -> Any:
|
|
299
|
+
negated = term.startswith('-')
|
|
300
|
+
name = term[1:] if negated else term
|
|
301
|
+
if name in local:
|
|
302
|
+
expr = local[name]
|
|
303
|
+
else:
|
|
304
|
+
info = self.destination_model.get_variable(name)
|
|
305
|
+
if info is None:
|
|
306
|
+
raise FlamaException(f'Unsupported feature: {name}')
|
|
307
|
+
expr = info.sel
|
|
308
|
+
return z3.Not(expr) if negated else expr
|
|
309
|
+
|
|
310
|
+
for clause in clauses:
|
|
311
|
+
literals = [literal_expr(term) for term in clause]
|
|
312
|
+
formula = literals[0] if len(literals) == 1 else z3.Or(*literals)
|
|
313
|
+
self.destination_model.add_constraint(formula)
|
|
314
|
+
|
|
264
315
|
def _get_expression(self, node: Node, parent: Optional[Node]) -> z3.ExprRef:
|
|
265
316
|
if node.is_term():
|
|
266
317
|
return self._get_term_expression(node, parent)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flamapy-z3
|
|
3
|
+
Version: 2.6.0
|
|
4
|
+
Summary: z3-plugin for the automated analysis of feature models
|
|
5
|
+
Author-email: Flamapy <flamapy@us.es>
|
|
6
|
+
License-Expression: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/flamapy/z3_metamodel
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: flamapy-fw~=2.6.0
|
|
11
|
+
Requires-Dist: flamapy-fm~=2.6.0
|
|
12
|
+
Requires-Dist: z3-solver~=4.14.1.0
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
16
|
+
Requires-Dist: prospector; extra == "dev"
|
|
17
|
+
Requires-Dist: mypy; extra == "dev"
|
|
18
|
+
Requires-Dist: coverage; extra == "dev"
|
|
19
|
+
|
|
20
|
+
# flamapy-z3
|
|
21
|
+
|
|
22
|
+
SMT-based analysis for [flamapy](https://flamapy.org) feature models with typed
|
|
23
|
+
attributes (Integer, Real, String), built on the [Z3](https://github.com/Z3Prover/z3)
|
|
24
|
+
solver.
|
|
25
|
+
|
|
26
|
+
**Documentation:** https://docs.flamapy.org/framework/plugins/z3_plugin
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install flamapy-z3
|
|
32
|
+
```
|
|
@@ -7,6 +7,7 @@ flamapy/metamodels/z3_metamodel/models/z3_model.py
|
|
|
7
7
|
flamapy/metamodels/z3_metamodel/operations/__init__.py
|
|
8
8
|
flamapy/metamodels/z3_metamodel/operations/z3_all_feature_bounds.py
|
|
9
9
|
flamapy/metamodels/z3_metamodel/operations/z3_attribute_optimization.py
|
|
10
|
+
flamapy/metamodels/z3_metamodel/operations/z3_backbone.py
|
|
10
11
|
flamapy/metamodels/z3_metamodel/operations/z3_configurations.py
|
|
11
12
|
flamapy/metamodels/z3_metamodel/operations/z3_configurations_number.py
|
|
12
13
|
flamapy/metamodels/z3_metamodel/operations/z3_core_features.py
|
|
@@ -25,4 +26,5 @@ flamapy_z3.egg-info/SOURCES.txt
|
|
|
25
26
|
flamapy_z3.egg-info/dependency_links.txt
|
|
26
27
|
flamapy_z3.egg-info/requires.txt
|
|
27
28
|
flamapy_z3.egg-info/top_level.txt
|
|
29
|
+
tests/test_tseytin.py
|
|
28
30
|
tests/test_z3_metamodel.py
|
|
@@ -4,15 +4,15 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "flamapy-z3"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.6.0"
|
|
8
8
|
description = "z3-plugin for the automated analysis of feature models"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "GPL-3.0-or-later"
|
|
11
11
|
authors = [{ name = "Flamapy", email = "flamapy@us.es" }]
|
|
12
|
-
requires-python = ">=3.
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
13
|
dependencies = [
|
|
14
|
-
"flamapy-fw~=2.
|
|
15
|
-
"flamapy-fm~=2.
|
|
14
|
+
"flamapy-fw~=2.6.0",
|
|
15
|
+
"flamapy-fm~=2.6.0",
|
|
16
16
|
"z3-solver~=4.14.1.0",
|
|
17
17
|
]
|
|
18
18
|
|
|
@@ -33,7 +33,7 @@ include = ["flamapy.*"]
|
|
|
33
33
|
|
|
34
34
|
[tool.ruff]
|
|
35
35
|
line-length = 100
|
|
36
|
-
target-version = "
|
|
36
|
+
target-version = "py311"
|
|
37
37
|
|
|
38
38
|
[tool.ruff.lint]
|
|
39
39
|
select = ["E", "W", "F", "C", "PL", "RUF"]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""The opt-in Tseytin CNF encoding must yield the same Z3 analysis results as the
|
|
2
|
+
default direct translation, and must fall back to the direct translation for any
|
|
3
|
+
constraint that is not purely propositional over boolean features."""
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
|
|
7
|
+
from flamapy.metamodels.fm_metamodel.transformations import UVLReader
|
|
8
|
+
from flamapy.metamodels.z3_metamodel.transformations import FmToZ3
|
|
9
|
+
from flamapy.metamodels.z3_metamodel.operations.z3_satisfiable import Z3Satisfiable
|
|
10
|
+
from flamapy.metamodels.z3_metamodel.operations.z3_configurations import Z3Configurations
|
|
11
|
+
from flamapy.metamodels.z3_metamodel.operations.z3_configurations_number import (
|
|
12
|
+
Z3ConfigurationsNumber,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Purely propositional model: every cross-tree constraint is boolean.
|
|
17
|
+
_PROP_UVL = """features
|
|
18
|
+
Root {abstract}
|
|
19
|
+
optional
|
|
20
|
+
A
|
|
21
|
+
B
|
|
22
|
+
C
|
|
23
|
+
D
|
|
24
|
+
constraints
|
|
25
|
+
(A | B) => (C <=> D)
|
|
26
|
+
A => !B
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# All-arithmetic constraints; the Tseytin path must fall back to direct translation.
|
|
30
|
+
_ARITH_MODEL = 'resources/models/uvl_models/fm03_integer_conditional_bounded.uvl'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _build_from_uvl_text(text, cnf_method):
|
|
34
|
+
handle, path = tempfile.mkstemp(suffix='.uvl')
|
|
35
|
+
try:
|
|
36
|
+
with os.fdopen(handle, 'w') as file:
|
|
37
|
+
file.write(text)
|
|
38
|
+
return FmToZ3(UVLReader(path).transform(), cnf_method=cnf_method).transform()
|
|
39
|
+
finally:
|
|
40
|
+
os.remove(path)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _projected(model):
|
|
44
|
+
configs = Z3Configurations().execute(model).get_result()
|
|
45
|
+
return {frozenset(k for k, v in c.elements.items() if v is True) for c in configs}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_tseytin_matches_direct_on_propositional_model() -> None:
|
|
49
|
+
direct = _build_from_uvl_text(_PROP_UVL, 'direct')
|
|
50
|
+
tseytin = _build_from_uvl_text(_PROP_UVL, 'tseytin')
|
|
51
|
+
|
|
52
|
+
assert Z3ConfigurationsNumber().execute(direct).get_result() == \
|
|
53
|
+
Z3ConfigurationsNumber().execute(tseytin).get_result()
|
|
54
|
+
assert _projected(direct) == _projected(tseytin)
|
|
55
|
+
assert tseytin.auxiliary_variables # gates were introduced
|
|
56
|
+
# Auxiliary variables must not have been registered as features.
|
|
57
|
+
assert not set(str(a) for a in tseytin.auxiliary_variables) & set(tseytin.features.keys())
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_tseytin_falls_back_for_arithmetic_constraints() -> None:
|
|
61
|
+
direct = FmToZ3(UVLReader(_ARITH_MODEL).transform(), cnf_method='direct').transform()
|
|
62
|
+
tseytin = FmToZ3(UVLReader(_ARITH_MODEL).transform(), cnf_method='tseytin').transform()
|
|
63
|
+
|
|
64
|
+
# Nothing was propositional, so no auxiliary variables were created.
|
|
65
|
+
assert tseytin.auxiliary_variables == []
|
|
66
|
+
assert Z3Satisfiable().execute(direct).get_result() == \
|
|
67
|
+
Z3Satisfiable().execute(tseytin).get_result()
|
|
68
|
+
assert Z3ConfigurationsNumber().execute(direct).get_result() == \
|
|
69
|
+
Z3ConfigurationsNumber().execute(tseytin).get_result()
|
|
@@ -53,7 +53,8 @@ def test_z3_satisfiable(path: str, expected: bool):
|
|
|
53
53
|
('resources/models/uvl_models/fm09_real_bounded.uvl', 6),
|
|
54
54
|
('resources/models/uvl_models/fm10_real_conditional_bounded.uvl', 3),
|
|
55
55
|
('resources/models/uvl_models/icecream_attributes.uvl', 15),
|
|
56
|
-
('resources/models/uvl_models/group_cardinalities.uvl', 16)
|
|
56
|
+
('resources/models/uvl_models/group_cardinalities.uvl', 16),
|
|
57
|
+
('resources/models/uvl_models/mutex.uvl', 6)
|
|
57
58
|
])
|
|
58
59
|
def test_nconfigs(path: str, expected: int):
|
|
59
60
|
z3_model = _read_model(path)
|
flamapy_z3-2.5.0.dev0/PKG-INFO
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: flamapy-z3
|
|
3
|
-
Version: 2.5.0.dev0
|
|
4
|
-
Summary: z3-plugin for the automated analysis of feature models
|
|
5
|
-
Author-email: Flamapy <flamapy@us.es>
|
|
6
|
-
License-Expression: GPL-3.0-or-later
|
|
7
|
-
Project-URL: Homepage, https://github.com/flamapy/z3_metamodel
|
|
8
|
-
Requires-Python: >=3.9
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
Requires-Dist: flamapy-fw~=2.5.0.dev0
|
|
11
|
-
Requires-Dist: flamapy-fm~=2.5.0.dev0
|
|
12
|
-
Requires-Dist: z3-solver~=4.14.1.0
|
|
13
|
-
Provides-Extra: dev
|
|
14
|
-
Requires-Dist: pytest; extra == "dev"
|
|
15
|
-
Requires-Dist: pytest-mock; extra == "dev"
|
|
16
|
-
Requires-Dist: prospector; extra == "dev"
|
|
17
|
-
Requires-Dist: mypy; extra == "dev"
|
|
18
|
-
Requires-Dist: coverage; extra == "dev"
|
|
19
|
-
|
|
20
|
-
# Automated Analysis of UVL using Satisfiability Modulo Theories
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## Description
|
|
24
|
-
This repository contains the plugin that supports z3 representations for feature models.
|
|
25
|
-
|
|
26
|
-
The plugin is based on [flamapy](https://flamapy.github.io/), and relies on the [Z3 solver](https://github.com/Z3Prover/z3?tab=readme-ov-file) library. The architecture is as follows:
|
|
27
|
-
|
|
28
|
-
<p align="center">
|
|
29
|
-
<img width="750" src="resources/images/z3metamodel.png">
|
|
30
|
-
</p>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## Requirements and Installation
|
|
34
|
-
- [Python 3.11+](https://www.python.org/)
|
|
35
|
-
- [Flamapy](https://www.flamapy.org/)
|
|
36
|
-
|
|
37
|
-
The framework has been tested in Linux and Windows 11 with Python 3.12. Python 3.13+ may not be still supported.
|
|
38
|
-
|
|
39
|
-
### Download and installation
|
|
40
|
-
1. Install [Python 3.11+](https://www.python.org/).
|
|
41
|
-
2. Download/Clone this repository and enter into the main directory.
|
|
42
|
-
3. Create a virtual environment: `python -m venv env`
|
|
43
|
-
4. Activate the environment:
|
|
44
|
-
|
|
45
|
-
In Linux: `source env/bin/activate`
|
|
46
|
-
|
|
47
|
-
In Windows: `.\env\Scripts\Activate`
|
|
48
|
-
|
|
49
|
-
5. Install dependencies (flamapy): `pip install -r requirements.txt`
|
|
50
|
-
|
|
51
|
-
** In case that you are running Ubuntu and get an error installing flamapy, please install the package python3-dev with the command `sudo apt update && sudo apt install python3-dev` and update wheel and setuptools with the command `pip install --upgrade pip wheel setuptools` before step 5.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
## Functionality and usage
|
|
55
|
-
The executable script [test.py](/test.py) serves as an entry point to show the plugin in action.
|
|
56
|
-
|
|
57
|
-
Simply run: `python test.py` to see it in action over the running feature model presented in the paper.
|
|
58
|
-
|
|
59
|
-
The following functionality is provided:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
### Load a feature model in UVL and translate to SMT
|
|
63
|
-
```python
|
|
64
|
-
from flamapy.metamodels.fm_metamodel.transformations import UVLReader
|
|
65
|
-
from flamapy.metamodels.z3_metamodel.transformations import FmToZ3
|
|
66
|
-
|
|
67
|
-
# Load the feature model from UVL
|
|
68
|
-
fm_model = UVLReader('resources/models/uvl_models/Pizza_z3.uvl').transform()
|
|
69
|
-
# Transform the feature model to SMT
|
|
70
|
-
z3_model = FmToZ3(fm_model).transform()
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Analysis operations
|
|
74
|
-
The following operations are available:
|
|
75
|
-
```python
|
|
76
|
-
from flamapy.metamodels.z3_metamodel.operations import (
|
|
77
|
-
Z3Satisfiable,
|
|
78
|
-
Z3Configurations,
|
|
79
|
-
Z3ConfigurationsNumber,
|
|
80
|
-
Z3CoreFeatures,
|
|
81
|
-
Z3DeadFeatures,
|
|
82
|
-
Z3FalseOptionalFeatures,
|
|
83
|
-
Z3AttributeOptimization,
|
|
84
|
-
Z3SatisfiableConfiguration,
|
|
85
|
-
Z3FeatureBounds,
|
|
86
|
-
Z3AllFeatureBounds,
|
|
87
|
-
)
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
- **Satisfiable**
|
|
91
|
-
|
|
92
|
-
Return whether the model is satisfiable (valid):
|
|
93
|
-
```python
|
|
94
|
-
satisfiable = Z3Satisfiable().execute(z3_model).get_result()
|
|
95
|
-
print(f'Satisfiable? (valid?): {satisfiable}')
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
- **Core features**
|
|
99
|
-
|
|
100
|
-
Return the core features of the model:
|
|
101
|
-
```python
|
|
102
|
-
core_features = Z3CoreFeatures().execute(z3_model).get_result()
|
|
103
|
-
print(f'Core features: {core_features}')
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
- **Dead features**
|
|
107
|
-
|
|
108
|
-
Return the dead features of the model:
|
|
109
|
-
```python
|
|
110
|
-
dead_features = Z3DeadFeatures().execute(z3_model).get_result()
|
|
111
|
-
print(f'Dead features: {dead_features}')
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
- **False-Optional features**
|
|
115
|
-
|
|
116
|
-
Return the false-optional features of the model:
|
|
117
|
-
```python
|
|
118
|
-
false_optional_features = Z3FalseOptionalFeatures().execute(z3_model).get_result()
|
|
119
|
-
print(f'False-optional features: {false_optional_features}')
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
- **Configurations**
|
|
123
|
-
|
|
124
|
-
Enumerate the configurations of the model:
|
|
125
|
-
```python
|
|
126
|
-
configurations = Z3Configurations().execute(z3_model).get_result()
|
|
127
|
-
print(f'Configurations: {len(configurations)}')
|
|
128
|
-
for i, config in enumerate(configurations, 1):
|
|
129
|
-
config_str = ', '.join(f'{f}={v}' if not isinstance(v, bool) else f'{f}' for f,v in config.elements.items() if config.is_selected(f))
|
|
130
|
-
print(f'Config. {i}: {config_str}')
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
- **Configurations number**
|
|
134
|
-
|
|
135
|
-
Return the number of configurations:
|
|
136
|
-
```python
|
|
137
|
-
n_configs = Z3ConfigurationsNumber().execute(z3_model).get_result()
|
|
138
|
-
print(f'Configurations number: {n_configs}')
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
- **Boundaries analysis of typed features**
|
|
142
|
-
|
|
143
|
-
Return the boundaries of the numerical features (Integer, Real, String) of the model:
|
|
144
|
-
```python
|
|
145
|
-
attributes = fm_model.get_attributes()
|
|
146
|
-
print('Attributes in the model')
|
|
147
|
-
for attr in attributes:
|
|
148
|
-
print(f' - {attr.name} ({attr.attribute_type})')
|
|
149
|
-
|
|
150
|
-
variable_bounds = Z3AllFeatureBounds().execute(z3_model).get_result()
|
|
151
|
-
print('Variable bounds for all typed variables:')
|
|
152
|
-
for var_name, bounds in variable_bounds.items():
|
|
153
|
-
print(f' - {var_name}: {bounds}')
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
- **Configuration optimization based on feature attributes:**
|
|
157
|
-
|
|
158
|
-
Return the set of configurations that optimize the given goals (i.e., the pareto front):
|
|
159
|
-
```python
|
|
160
|
-
attribute_optimization_op = Z3AttributeOptimization()
|
|
161
|
-
attributes = {'Price': OptimizationGoal.MAXIMIZE,
|
|
162
|
-
'Kcal': OptimizationGoal.MINIMIZE}
|
|
163
|
-
attribute_optimization_op.set_attributes(attributes)
|
|
164
|
-
configurations_with_values = attribute_optimization_op.execute(z3_model).get_result()
|
|
165
|
-
print(f'Optimum configurations: {len(configurations_with_values)} configs.')
|
|
166
|
-
for i, config_value in enumerate(configurations_with_values, 1):
|
|
167
|
-
config, values = config_value
|
|
168
|
-
config_str = ', '.join(f'{f}={v}' if not isinstance(v, bool) else f'{f}' for f,v in config.elements.items() if config.is_selected(f))
|
|
169
|
-
values_str = ', '.join(f'{k}={v}' for k,v in values.items())
|
|
170
|
-
print(f'Config. {i}: {config_str} | Values: {values_str}')
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
- Configuration validation:
|
|
174
|
-
|
|
175
|
-
Return whether a given partial or full configuration is valid:
|
|
176
|
-
```python
|
|
177
|
-
from flamapy.metamodels.configuration_metamodel.transformations import ConfigurationJSONReader
|
|
178
|
-
configuration = ConfigurationJSONReader('resources/configs/pizza_z3_config1.json').transform()
|
|
179
|
-
configuration.set_full(False)
|
|
180
|
-
print(f'Configuration: {configuration.elements}')
|
|
181
|
-
satisfiable_configuration_op = Z3SatisfiableConfiguration()
|
|
182
|
-
satisfiable_configuration_op.set_configuration(configuration)
|
|
183
|
-
is_satisfiable = satisfiable_configuration_op.execute(z3_model).get_result()
|
|
184
|
-
print(f'Is the configuration satisfiable? {is_satisfiable}')
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
**Note:** The Z3Configurations and Z3ConfigurationsNumber operations may takes longer if the number of configuration is huge, or even not finish if the model is unbounded.
|
|
188
|
-
|
|
189
|
-
**Note:** The Z3Configurations and Z3ConfigurationsNumber operations support also a partial configuration as an additional argument, so the operation will return the result taking into account the given partial configuration.
|
|
190
|
-
For example:
|
|
191
|
-
|
|
192
|
-
```python
|
|
193
|
-
from flamapy.core.models import Configuration
|
|
194
|
-
# Create a partial configuration
|
|
195
|
-
elements = {'Pizza': True, 'SpicyLvl': 5}
|
|
196
|
-
partial_config = Configuration(elements)
|
|
197
|
-
# Calculate the number of configuration from the partial configuration
|
|
198
|
-
configs_number_op = Z3ConfigurationsNumber()
|
|
199
|
-
configs_number_op.set_partial_configuration(partial_config)
|
|
200
|
-
n_configs = configs_number_op.execute(z3_model).get_result()
|
|
201
|
-
print(f'#Configurations: {n_configs}')
|
|
202
|
-
```
|
flamapy_z3-2.5.0.dev0/README.md
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
# Automated Analysis of UVL using Satisfiability Modulo Theories
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
## Description
|
|
5
|
-
This repository contains the plugin that supports z3 representations for feature models.
|
|
6
|
-
|
|
7
|
-
The plugin is based on [flamapy](https://flamapy.github.io/), and relies on the [Z3 solver](https://github.com/Z3Prover/z3?tab=readme-ov-file) library. The architecture is as follows:
|
|
8
|
-
|
|
9
|
-
<p align="center">
|
|
10
|
-
<img width="750" src="resources/images/z3metamodel.png">
|
|
11
|
-
</p>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## Requirements and Installation
|
|
15
|
-
- [Python 3.11+](https://www.python.org/)
|
|
16
|
-
- [Flamapy](https://www.flamapy.org/)
|
|
17
|
-
|
|
18
|
-
The framework has been tested in Linux and Windows 11 with Python 3.12. Python 3.13+ may not be still supported.
|
|
19
|
-
|
|
20
|
-
### Download and installation
|
|
21
|
-
1. Install [Python 3.11+](https://www.python.org/).
|
|
22
|
-
2. Download/Clone this repository and enter into the main directory.
|
|
23
|
-
3. Create a virtual environment: `python -m venv env`
|
|
24
|
-
4. Activate the environment:
|
|
25
|
-
|
|
26
|
-
In Linux: `source env/bin/activate`
|
|
27
|
-
|
|
28
|
-
In Windows: `.\env\Scripts\Activate`
|
|
29
|
-
|
|
30
|
-
5. Install dependencies (flamapy): `pip install -r requirements.txt`
|
|
31
|
-
|
|
32
|
-
** In case that you are running Ubuntu and get an error installing flamapy, please install the package python3-dev with the command `sudo apt update && sudo apt install python3-dev` and update wheel and setuptools with the command `pip install --upgrade pip wheel setuptools` before step 5.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
## Functionality and usage
|
|
36
|
-
The executable script [test.py](/test.py) serves as an entry point to show the plugin in action.
|
|
37
|
-
|
|
38
|
-
Simply run: `python test.py` to see it in action over the running feature model presented in the paper.
|
|
39
|
-
|
|
40
|
-
The following functionality is provided:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
### Load a feature model in UVL and translate to SMT
|
|
44
|
-
```python
|
|
45
|
-
from flamapy.metamodels.fm_metamodel.transformations import UVLReader
|
|
46
|
-
from flamapy.metamodels.z3_metamodel.transformations import FmToZ3
|
|
47
|
-
|
|
48
|
-
# Load the feature model from UVL
|
|
49
|
-
fm_model = UVLReader('resources/models/uvl_models/Pizza_z3.uvl').transform()
|
|
50
|
-
# Transform the feature model to SMT
|
|
51
|
-
z3_model = FmToZ3(fm_model).transform()
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Analysis operations
|
|
55
|
-
The following operations are available:
|
|
56
|
-
```python
|
|
57
|
-
from flamapy.metamodels.z3_metamodel.operations import (
|
|
58
|
-
Z3Satisfiable,
|
|
59
|
-
Z3Configurations,
|
|
60
|
-
Z3ConfigurationsNumber,
|
|
61
|
-
Z3CoreFeatures,
|
|
62
|
-
Z3DeadFeatures,
|
|
63
|
-
Z3FalseOptionalFeatures,
|
|
64
|
-
Z3AttributeOptimization,
|
|
65
|
-
Z3SatisfiableConfiguration,
|
|
66
|
-
Z3FeatureBounds,
|
|
67
|
-
Z3AllFeatureBounds,
|
|
68
|
-
)
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
- **Satisfiable**
|
|
72
|
-
|
|
73
|
-
Return whether the model is satisfiable (valid):
|
|
74
|
-
```python
|
|
75
|
-
satisfiable = Z3Satisfiable().execute(z3_model).get_result()
|
|
76
|
-
print(f'Satisfiable? (valid?): {satisfiable}')
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
- **Core features**
|
|
80
|
-
|
|
81
|
-
Return the core features of the model:
|
|
82
|
-
```python
|
|
83
|
-
core_features = Z3CoreFeatures().execute(z3_model).get_result()
|
|
84
|
-
print(f'Core features: {core_features}')
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
- **Dead features**
|
|
88
|
-
|
|
89
|
-
Return the dead features of the model:
|
|
90
|
-
```python
|
|
91
|
-
dead_features = Z3DeadFeatures().execute(z3_model).get_result()
|
|
92
|
-
print(f'Dead features: {dead_features}')
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
- **False-Optional features**
|
|
96
|
-
|
|
97
|
-
Return the false-optional features of the model:
|
|
98
|
-
```python
|
|
99
|
-
false_optional_features = Z3FalseOptionalFeatures().execute(z3_model).get_result()
|
|
100
|
-
print(f'False-optional features: {false_optional_features}')
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
- **Configurations**
|
|
104
|
-
|
|
105
|
-
Enumerate the configurations of the model:
|
|
106
|
-
```python
|
|
107
|
-
configurations = Z3Configurations().execute(z3_model).get_result()
|
|
108
|
-
print(f'Configurations: {len(configurations)}')
|
|
109
|
-
for i, config in enumerate(configurations, 1):
|
|
110
|
-
config_str = ', '.join(f'{f}={v}' if not isinstance(v, bool) else f'{f}' for f,v in config.elements.items() if config.is_selected(f))
|
|
111
|
-
print(f'Config. {i}: {config_str}')
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
- **Configurations number**
|
|
115
|
-
|
|
116
|
-
Return the number of configurations:
|
|
117
|
-
```python
|
|
118
|
-
n_configs = Z3ConfigurationsNumber().execute(z3_model).get_result()
|
|
119
|
-
print(f'Configurations number: {n_configs}')
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
- **Boundaries analysis of typed features**
|
|
123
|
-
|
|
124
|
-
Return the boundaries of the numerical features (Integer, Real, String) of the model:
|
|
125
|
-
```python
|
|
126
|
-
attributes = fm_model.get_attributes()
|
|
127
|
-
print('Attributes in the model')
|
|
128
|
-
for attr in attributes:
|
|
129
|
-
print(f' - {attr.name} ({attr.attribute_type})')
|
|
130
|
-
|
|
131
|
-
variable_bounds = Z3AllFeatureBounds().execute(z3_model).get_result()
|
|
132
|
-
print('Variable bounds for all typed variables:')
|
|
133
|
-
for var_name, bounds in variable_bounds.items():
|
|
134
|
-
print(f' - {var_name}: {bounds}')
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
- **Configuration optimization based on feature attributes:**
|
|
138
|
-
|
|
139
|
-
Return the set of configurations that optimize the given goals (i.e., the pareto front):
|
|
140
|
-
```python
|
|
141
|
-
attribute_optimization_op = Z3AttributeOptimization()
|
|
142
|
-
attributes = {'Price': OptimizationGoal.MAXIMIZE,
|
|
143
|
-
'Kcal': OptimizationGoal.MINIMIZE}
|
|
144
|
-
attribute_optimization_op.set_attributes(attributes)
|
|
145
|
-
configurations_with_values = attribute_optimization_op.execute(z3_model).get_result()
|
|
146
|
-
print(f'Optimum configurations: {len(configurations_with_values)} configs.')
|
|
147
|
-
for i, config_value in enumerate(configurations_with_values, 1):
|
|
148
|
-
config, values = config_value
|
|
149
|
-
config_str = ', '.join(f'{f}={v}' if not isinstance(v, bool) else f'{f}' for f,v in config.elements.items() if config.is_selected(f))
|
|
150
|
-
values_str = ', '.join(f'{k}={v}' for k,v in values.items())
|
|
151
|
-
print(f'Config. {i}: {config_str} | Values: {values_str}')
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
- Configuration validation:
|
|
155
|
-
|
|
156
|
-
Return whether a given partial or full configuration is valid:
|
|
157
|
-
```python
|
|
158
|
-
from flamapy.metamodels.configuration_metamodel.transformations import ConfigurationJSONReader
|
|
159
|
-
configuration = ConfigurationJSONReader('resources/configs/pizza_z3_config1.json').transform()
|
|
160
|
-
configuration.set_full(False)
|
|
161
|
-
print(f'Configuration: {configuration.elements}')
|
|
162
|
-
satisfiable_configuration_op = Z3SatisfiableConfiguration()
|
|
163
|
-
satisfiable_configuration_op.set_configuration(configuration)
|
|
164
|
-
is_satisfiable = satisfiable_configuration_op.execute(z3_model).get_result()
|
|
165
|
-
print(f'Is the configuration satisfiable? {is_satisfiable}')
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
**Note:** The Z3Configurations and Z3ConfigurationsNumber operations may takes longer if the number of configuration is huge, or even not finish if the model is unbounded.
|
|
169
|
-
|
|
170
|
-
**Note:** The Z3Configurations and Z3ConfigurationsNumber operations support also a partial configuration as an additional argument, so the operation will return the result taking into account the given partial configuration.
|
|
171
|
-
For example:
|
|
172
|
-
|
|
173
|
-
```python
|
|
174
|
-
from flamapy.core.models import Configuration
|
|
175
|
-
# Create a partial configuration
|
|
176
|
-
elements = {'Pizza': True, 'SpicyLvl': 5}
|
|
177
|
-
partial_config = Configuration(elements)
|
|
178
|
-
# Calculate the number of configuration from the partial configuration
|
|
179
|
-
configs_number_op = Z3ConfigurationsNumber()
|
|
180
|
-
configs_number_op.set_partial_configuration(partial_config)
|
|
181
|
-
n_configs = configs_number_op.execute(z3_model).get_result()
|
|
182
|
-
print(f'#Configurations: {n_configs}')
|
|
183
|
-
```
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from abc import abstractmethod
|
|
2
|
-
from enum import Enum
|
|
3
|
-
|
|
4
|
-
from flamapy.core.operations import Operation
|
|
5
|
-
from flamapy.metamodels.configuration_metamodel.models import Configuration
|
|
6
|
-
from flamapy.metamodels.fm_metamodel.models import Attribute
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class OptimizationGoal(Enum):
|
|
10
|
-
MAXIMIZE = 'Maximize'
|
|
11
|
-
MINIMIZE = 'Minimize'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class AttributeOptimization(Operation):
|
|
15
|
-
"""This operation returns the configurations that optimize the given attribute(s)."""
|
|
16
|
-
|
|
17
|
-
@abstractmethod
|
|
18
|
-
def __init__(self) -> None:
|
|
19
|
-
pass
|
|
20
|
-
|
|
21
|
-
@abstractmethod
|
|
22
|
-
def set_attributes(self, attributes: dict[Attribute, OptimizationGoal]) -> None:
|
|
23
|
-
pass
|
|
24
|
-
|
|
25
|
-
@abstractmethod
|
|
26
|
-
def optimize(self) -> list[Configuration]:
|
|
27
|
-
pass
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: flamapy-z3
|
|
3
|
-
Version: 2.5.0.dev0
|
|
4
|
-
Summary: z3-plugin for the automated analysis of feature models
|
|
5
|
-
Author-email: Flamapy <flamapy@us.es>
|
|
6
|
-
License-Expression: GPL-3.0-or-later
|
|
7
|
-
Project-URL: Homepage, https://github.com/flamapy/z3_metamodel
|
|
8
|
-
Requires-Python: >=3.9
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
Requires-Dist: flamapy-fw~=2.5.0.dev0
|
|
11
|
-
Requires-Dist: flamapy-fm~=2.5.0.dev0
|
|
12
|
-
Requires-Dist: z3-solver~=4.14.1.0
|
|
13
|
-
Provides-Extra: dev
|
|
14
|
-
Requires-Dist: pytest; extra == "dev"
|
|
15
|
-
Requires-Dist: pytest-mock; extra == "dev"
|
|
16
|
-
Requires-Dist: prospector; extra == "dev"
|
|
17
|
-
Requires-Dist: mypy; extra == "dev"
|
|
18
|
-
Requires-Dist: coverage; extra == "dev"
|
|
19
|
-
|
|
20
|
-
# Automated Analysis of UVL using Satisfiability Modulo Theories
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## Description
|
|
24
|
-
This repository contains the plugin that supports z3 representations for feature models.
|
|
25
|
-
|
|
26
|
-
The plugin is based on [flamapy](https://flamapy.github.io/), and relies on the [Z3 solver](https://github.com/Z3Prover/z3?tab=readme-ov-file) library. The architecture is as follows:
|
|
27
|
-
|
|
28
|
-
<p align="center">
|
|
29
|
-
<img width="750" src="resources/images/z3metamodel.png">
|
|
30
|
-
</p>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## Requirements and Installation
|
|
34
|
-
- [Python 3.11+](https://www.python.org/)
|
|
35
|
-
- [Flamapy](https://www.flamapy.org/)
|
|
36
|
-
|
|
37
|
-
The framework has been tested in Linux and Windows 11 with Python 3.12. Python 3.13+ may not be still supported.
|
|
38
|
-
|
|
39
|
-
### Download and installation
|
|
40
|
-
1. Install [Python 3.11+](https://www.python.org/).
|
|
41
|
-
2. Download/Clone this repository and enter into the main directory.
|
|
42
|
-
3. Create a virtual environment: `python -m venv env`
|
|
43
|
-
4. Activate the environment:
|
|
44
|
-
|
|
45
|
-
In Linux: `source env/bin/activate`
|
|
46
|
-
|
|
47
|
-
In Windows: `.\env\Scripts\Activate`
|
|
48
|
-
|
|
49
|
-
5. Install dependencies (flamapy): `pip install -r requirements.txt`
|
|
50
|
-
|
|
51
|
-
** In case that you are running Ubuntu and get an error installing flamapy, please install the package python3-dev with the command `sudo apt update && sudo apt install python3-dev` and update wheel and setuptools with the command `pip install --upgrade pip wheel setuptools` before step 5.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
## Functionality and usage
|
|
55
|
-
The executable script [test.py](/test.py) serves as an entry point to show the plugin in action.
|
|
56
|
-
|
|
57
|
-
Simply run: `python test.py` to see it in action over the running feature model presented in the paper.
|
|
58
|
-
|
|
59
|
-
The following functionality is provided:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
### Load a feature model in UVL and translate to SMT
|
|
63
|
-
```python
|
|
64
|
-
from flamapy.metamodels.fm_metamodel.transformations import UVLReader
|
|
65
|
-
from flamapy.metamodels.z3_metamodel.transformations import FmToZ3
|
|
66
|
-
|
|
67
|
-
# Load the feature model from UVL
|
|
68
|
-
fm_model = UVLReader('resources/models/uvl_models/Pizza_z3.uvl').transform()
|
|
69
|
-
# Transform the feature model to SMT
|
|
70
|
-
z3_model = FmToZ3(fm_model).transform()
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Analysis operations
|
|
74
|
-
The following operations are available:
|
|
75
|
-
```python
|
|
76
|
-
from flamapy.metamodels.z3_metamodel.operations import (
|
|
77
|
-
Z3Satisfiable,
|
|
78
|
-
Z3Configurations,
|
|
79
|
-
Z3ConfigurationsNumber,
|
|
80
|
-
Z3CoreFeatures,
|
|
81
|
-
Z3DeadFeatures,
|
|
82
|
-
Z3FalseOptionalFeatures,
|
|
83
|
-
Z3AttributeOptimization,
|
|
84
|
-
Z3SatisfiableConfiguration,
|
|
85
|
-
Z3FeatureBounds,
|
|
86
|
-
Z3AllFeatureBounds,
|
|
87
|
-
)
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
- **Satisfiable**
|
|
91
|
-
|
|
92
|
-
Return whether the model is satisfiable (valid):
|
|
93
|
-
```python
|
|
94
|
-
satisfiable = Z3Satisfiable().execute(z3_model).get_result()
|
|
95
|
-
print(f'Satisfiable? (valid?): {satisfiable}')
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
- **Core features**
|
|
99
|
-
|
|
100
|
-
Return the core features of the model:
|
|
101
|
-
```python
|
|
102
|
-
core_features = Z3CoreFeatures().execute(z3_model).get_result()
|
|
103
|
-
print(f'Core features: {core_features}')
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
- **Dead features**
|
|
107
|
-
|
|
108
|
-
Return the dead features of the model:
|
|
109
|
-
```python
|
|
110
|
-
dead_features = Z3DeadFeatures().execute(z3_model).get_result()
|
|
111
|
-
print(f'Dead features: {dead_features}')
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
- **False-Optional features**
|
|
115
|
-
|
|
116
|
-
Return the false-optional features of the model:
|
|
117
|
-
```python
|
|
118
|
-
false_optional_features = Z3FalseOptionalFeatures().execute(z3_model).get_result()
|
|
119
|
-
print(f'False-optional features: {false_optional_features}')
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
- **Configurations**
|
|
123
|
-
|
|
124
|
-
Enumerate the configurations of the model:
|
|
125
|
-
```python
|
|
126
|
-
configurations = Z3Configurations().execute(z3_model).get_result()
|
|
127
|
-
print(f'Configurations: {len(configurations)}')
|
|
128
|
-
for i, config in enumerate(configurations, 1):
|
|
129
|
-
config_str = ', '.join(f'{f}={v}' if not isinstance(v, bool) else f'{f}' for f,v in config.elements.items() if config.is_selected(f))
|
|
130
|
-
print(f'Config. {i}: {config_str}')
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
- **Configurations number**
|
|
134
|
-
|
|
135
|
-
Return the number of configurations:
|
|
136
|
-
```python
|
|
137
|
-
n_configs = Z3ConfigurationsNumber().execute(z3_model).get_result()
|
|
138
|
-
print(f'Configurations number: {n_configs}')
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
- **Boundaries analysis of typed features**
|
|
142
|
-
|
|
143
|
-
Return the boundaries of the numerical features (Integer, Real, String) of the model:
|
|
144
|
-
```python
|
|
145
|
-
attributes = fm_model.get_attributes()
|
|
146
|
-
print('Attributes in the model')
|
|
147
|
-
for attr in attributes:
|
|
148
|
-
print(f' - {attr.name} ({attr.attribute_type})')
|
|
149
|
-
|
|
150
|
-
variable_bounds = Z3AllFeatureBounds().execute(z3_model).get_result()
|
|
151
|
-
print('Variable bounds for all typed variables:')
|
|
152
|
-
for var_name, bounds in variable_bounds.items():
|
|
153
|
-
print(f' - {var_name}: {bounds}')
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
- **Configuration optimization based on feature attributes:**
|
|
157
|
-
|
|
158
|
-
Return the set of configurations that optimize the given goals (i.e., the pareto front):
|
|
159
|
-
```python
|
|
160
|
-
attribute_optimization_op = Z3AttributeOptimization()
|
|
161
|
-
attributes = {'Price': OptimizationGoal.MAXIMIZE,
|
|
162
|
-
'Kcal': OptimizationGoal.MINIMIZE}
|
|
163
|
-
attribute_optimization_op.set_attributes(attributes)
|
|
164
|
-
configurations_with_values = attribute_optimization_op.execute(z3_model).get_result()
|
|
165
|
-
print(f'Optimum configurations: {len(configurations_with_values)} configs.')
|
|
166
|
-
for i, config_value in enumerate(configurations_with_values, 1):
|
|
167
|
-
config, values = config_value
|
|
168
|
-
config_str = ', '.join(f'{f}={v}' if not isinstance(v, bool) else f'{f}' for f,v in config.elements.items() if config.is_selected(f))
|
|
169
|
-
values_str = ', '.join(f'{k}={v}' for k,v in values.items())
|
|
170
|
-
print(f'Config. {i}: {config_str} | Values: {values_str}')
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
- Configuration validation:
|
|
174
|
-
|
|
175
|
-
Return whether a given partial or full configuration is valid:
|
|
176
|
-
```python
|
|
177
|
-
from flamapy.metamodels.configuration_metamodel.transformations import ConfigurationJSONReader
|
|
178
|
-
configuration = ConfigurationJSONReader('resources/configs/pizza_z3_config1.json').transform()
|
|
179
|
-
configuration.set_full(False)
|
|
180
|
-
print(f'Configuration: {configuration.elements}')
|
|
181
|
-
satisfiable_configuration_op = Z3SatisfiableConfiguration()
|
|
182
|
-
satisfiable_configuration_op.set_configuration(configuration)
|
|
183
|
-
is_satisfiable = satisfiable_configuration_op.execute(z3_model).get_result()
|
|
184
|
-
print(f'Is the configuration satisfiable? {is_satisfiable}')
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
**Note:** The Z3Configurations and Z3ConfigurationsNumber operations may takes longer if the number of configuration is huge, or even not finish if the model is unbounded.
|
|
188
|
-
|
|
189
|
-
**Note:** The Z3Configurations and Z3ConfigurationsNumber operations support also a partial configuration as an additional argument, so the operation will return the result taking into account the given partial configuration.
|
|
190
|
-
For example:
|
|
191
|
-
|
|
192
|
-
```python
|
|
193
|
-
from flamapy.core.models import Configuration
|
|
194
|
-
# Create a partial configuration
|
|
195
|
-
elements = {'Pizza': True, 'SpicyLvl': 5}
|
|
196
|
-
partial_config = Configuration(elements)
|
|
197
|
-
# Calculate the number of configuration from the partial configuration
|
|
198
|
-
configs_number_op = Z3ConfigurationsNumber()
|
|
199
|
-
configs_number_op.set_partial_configuration(partial_config)
|
|
200
|
-
n_configs = configs_number_op.execute(z3_model).get_result()
|
|
201
|
-
print(f'#Configurations: {n_configs}')
|
|
202
|
-
```
|
|
File without changes
|
{flamapy_z3-2.5.0.dev0 → flamapy_z3-2.6.0}/flamapy/metamodels/z3_metamodel/models/__init__.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
|