Skip to main content

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

  1. Install Python 3.11+.

  2. Download/Clone this repository and enter into the main directory.

  3. Create a virtual environment: python -m venv env

  4. Activate the environment:

    In Linux: source env/bin/activate

    In Windows: .\env\Scripts\Activate

  5. 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-dev and update wheel and setuptools with the command pip install --upgrade pip wheel setuptools before 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flamapy_z3-2.1.0.dev2.tar.gz (23.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flamapy_z3-2.1.0.dev2-py3-none-any.whl (29.0 kB view details)

Uploaded Python 3

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

Hashes for flamapy_z3-2.1.0.dev2.tar.gz
Algorithm Hash digest
SHA256 5b07fbf31c4360e9d4f108b0854dc09d83cbf302855ff846102364786465fd64
MD5 0a91b1ae9de128be7e91d007cc524870
BLAKE2b-256 aec14a1a51e52e8fc7795505d75cc9f7992026ddd2f15054ea341245c84e05ef

See more details on using hashes here.

File details

Details for the file flamapy_z3-2.1.0.dev2-py3-none-any.whl.

File metadata

File hashes

Hashes for flamapy_z3-2.1.0.dev2-py3-none-any.whl
Algorithm Hash digest
SHA256 fc1bff99593611bd8d91f0bb895d6a7f111f1b74c050b7d36b24b8d8f53cc746
MD5 ee3953e73c52d223d6f7fa551c30bddf
BLAKE2b-256 9963500c7f6300784d4e5da06eb8ee8a5b8004c0ea191b64e3e4f2138748889e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page