z3-plugin for the automated analysis of feature models
Project description
Automated Analysis of UVL using Satisfiability Modulo Theories
Description
This repository contains the plugin that supports z3 representations for feature models.
The plugin is based on flamapy, and relies on the Z3 solver library. The architecture is as follows:
Requirements and Installation
The framework has been tested in Linux and Windows 11 with Python 3.12. Python 3.13+ may not be still supported.
Download and installation
-
Install Python 3.11+.
-
Download/Clone this repository and enter into the main directory.
-
Create a virtual environment:
python -m venv env -
Activate the environment:
In Linux:
source env/bin/activateIn Windows:
.\env\Scripts\Activate -
Install dependencies (flamapy):
pip install -r requirements.txt** 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-devand update wheel and setuptools with the commandpip install --upgrade pip wheel setuptoolsbefore step 5.
Functionality and usage
The executable script test.py serves as an entry point to show the plugin in action.
Simply run: python test.py to see it in action over the running feature model presented in the paper.
The following functionality is provided:
Load a feature model in UVL and translate to SMT
from flamapy.metamodels.fm_metamodel.transformations import UVLReader
from flamapy.metamodels.z3_metamodel.transformations import FmToZ3
# Load the feature model from UVL
fm_model = UVLReader('resources/models/uvl_models/Pizza_z3.uvl').transform()
# Transform the feature model to SMT
z3_model = FmToZ3(fm_model).transform()
Analysis operations
The following operations are available:
from flamapy.metamodels.z3_metamodel.operations import (
Z3Satisfiable,
Z3Configurations,
Z3ConfigurationsNumber,
Z3CoreFeatures,
Z3DeadFeatures,
Z3FalseOptionalFeatures,
Z3AttributeOptimization,
Z3SatisfiableConfiguration,
Z3FeatureBounds,
Z3AllFeatureBounds,
)
-
Satisfiable
Return whether the model is satisfiable (valid):
satisfiable = Z3Satisfiable().execute(z3_model).get_result() print(f'Satisfiable? (valid?): {satisfiable}')
-
Core features
Return the core features of the model:
core_features = Z3CoreFeatures().execute(z3_model).get_result() print(f'Core features: {core_features}')
-
Dead features
Return the dead features of the model:
dead_features = Z3DeadFeatures().execute(z3_model).get_result() print(f'Dead features: {dead_features}')
-
False-Optional features
Return the false-optional features of the model:
false_optional_features = Z3FalseOptionalFeatures().execute(z3_model).get_result() print(f'False-optional features: {false_optional_features}')
-
Configurations
Enumerate the configurations of the model:
configurations = Z3Configurations().execute(z3_model).get_result() print(f'Configurations: {len(configurations)}') for i, config in enumerate(configurations, 1): 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)) print(f'Config. {i}: {config_str}')
-
Configurations number
Return the number of configurations:
n_configs = Z3ConfigurationsNumber().execute(z3_model).get_result() print(f'Configurations number: {n_configs}')
-
Boundaries analysis of typed features
Return the boundaries of the numerical features (Integer, Real, String) of the model:
attributes = fm_model.get_attributes() print('Attributes in the model') for attr in attributes: print(f' - {attr.name} ({attr.attribute_type})') variable_bounds = Z3AllFeatureBounds().execute(z3_model).get_result() print('Variable bounds for all typed variables:') for var_name, bounds in variable_bounds.items(): print(f' - {var_name}: {bounds}')
-
Configuration optimization based on feature attributes:
Return the set of configurations that optimize the given goals (i.e., the pareto front):
attribute_optimization_op = Z3AttributeOptimization() attributes = {'Price': OptimizationGoal.MAXIMIZE, 'Kcal': OptimizationGoal.MINIMIZE} attribute_optimization_op.set_attributes(attributes) configurations_with_values = attribute_optimization_op.execute(z3_model).get_result() print(f'Optimum configurations: {len(configurations_with_values)} configs.') for i, config_value in enumerate(configurations_with_values, 1): config, values = config_value 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)) values_str = ', '.join(f'{k}={v}' for k,v in values.items()) print(f'Config. {i}: {config_str} | Values: {values_str}')
-
Configuration validation:
Return whether a given partial or full configuration is valid:
from flamapy.metamodels.configuration_metamodel.transformations import ConfigurationJSONReader configuration = ConfigurationJSONReader('resources/configs/pizza_z3_config1.json').transform() configuration.set_full(False) print(f'Configuration: {configuration.elements}') satisfiable_configuration_op = Z3SatisfiableConfiguration() satisfiable_configuration_op.set_configuration(configuration) is_satisfiable = satisfiable_configuration_op.execute(z3_model).get_result() print(f'Is the configuration satisfiable? {is_satisfiable}')
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.
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. For example:
from flamapy.core.models import Configuration
# Create a partial configuration
elements = {'Pizza': True, 'SpicyLvl': 5}
partial_config = Configuration(elements)
# Calculate the number of configuration from the partial configuration
configs_number_op = Z3ConfigurationsNumber()
configs_number_op.set_partial_configuration(partial_config)
n_configs = configs_number_op.execute(z3_model).get_result()
print(f'#Configurations: {n_configs}')
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flamapy_z3-2.1.0.dev2.tar.gz.
File metadata
- Download URL: flamapy_z3-2.1.0.dev2.tar.gz
- Upload date:
- Size: 23.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b07fbf31c4360e9d4f108b0854dc09d83cbf302855ff846102364786465fd64
|
|
| MD5 |
0a91b1ae9de128be7e91d007cc524870
|
|
| BLAKE2b-256 |
aec14a1a51e52e8fc7795505d75cc9f7992026ddd2f15054ea341245c84e05ef
|
File details
Details for the file flamapy_z3-2.1.0.dev2-py3-none-any.whl.
File metadata
- Download URL: flamapy_z3-2.1.0.dev2-py3-none-any.whl
- Upload date:
- Size: 29.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc1bff99593611bd8d91f0bb895d6a7f111f1b74c050b7d36b24b8d8f53cc746
|
|
| MD5 |
ee3953e73c52d223d6f7fa551c30bddf
|
|
| BLAKE2b-256 |
9963500c7f6300784d4e5da06eb8ee8a5b8004c0ea191b64e3e4f2138748889e
|