diff --git a/flamapy/metamodels/bdd_metamodel/operations/bdd_feature_inclusion_probability.py b/flamapy/metamodels/bdd_metamodel/operations/bdd_feature_inclusion_probability.py index b2938ce..556f0ab 100644 --- a/flamapy/metamodels/bdd_metamodel/operations/bdd_feature_inclusion_probability.py +++ b/flamapy/metamodels/bdd_metamodel/operations/bdd_feature_inclusion_probability.py @@ -1,7 +1,7 @@ -from typing import Optional +from typing import Any, Optional, cast +from flamapy.core.models import VariabilityModel from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration - from flamapy.metamodels.bdd_metamodel.models import BDDModel from flamapy.metamodels.bdd_metamodel.operations.interfaces import FeatureInclusionProbability from flamapy.metamodels.bdd_metamodel.operations import BDDProducts @@ -19,24 +19,23 @@ class BDDFeatureInclusionProbability(FeatureInclusionProbability): """ def __init__(self, partial_configuration: Optional[Configuration] = None) -> None: - self.bdd_model = None - self.result: dict[str, float] = {} + self.result: dict[Any, float] = {} self.partial_configuration = partial_configuration - def execute(self, model: BDDModel) -> 'BDDFeatureInclusionProbability': - self.bdd_model = model - self.result = feature_inclusion_probability(self.bdd_model, self.partial_configuration) + def execute(self, model: VariabilityModel) -> 'BDDFeatureInclusionProbability': + bdd_model = cast(BDDModel, model) + self.result = feature_inclusion_probability(bdd_model, self.partial_configuration) return self - def get_result(self) -> dict[str, float]: + def get_result(self) -> dict[Any, float]: return self.result - def feature_inclusion_probability(self) -> dict[str, float]: - return feature_inclusion_probability(self.bdd_model, self.partial_configuration) + def feature_inclusion_probability(self) -> dict[Any, float]: + return self.get_result() def feature_inclusion_probability(bdd_model: BDDModel, - config: Optional[Configuration] = None) -> dict[str, float]: + config: Optional[Configuration] = None) -> dict[Any, float]: products = BDDProducts(config).execute(bdd_model).get_result() n_products = len(products) if n_products == 0: diff --git a/flamapy/metamodels/bdd_metamodel/operations/bdd_product_distribution.py b/flamapy/metamodels/bdd_metamodel/operations/bdd_product_distribution.py index 27edc74..b735501 100644 --- a/flamapy/metamodels/bdd_metamodel/operations/bdd_product_distribution.py +++ b/flamapy/metamodels/bdd_metamodel/operations/bdd_product_distribution.py @@ -1,8 +1,10 @@ import math from collections import defaultdict +from typing import cast from dd.autoref import Function +from flamapy.core.models import VariabilityModel from flamapy.metamodels.bdd_metamodel.models import BDDModel from flamapy.metamodels.bdd_metamodel.operations.interfaces import ProductDistribution @@ -11,18 +13,17 @@ class BDDProductDistribution(ProductDistribution): def __init__(self) -> None: self.result: list[int] = [] - self.bdd_model = None - def execute(self, model: BDDModel) -> 'BDDProductDistribution': - self.bdd_model = model - self.result = product_distribution(self.bdd_model) + def execute(self, model: VariabilityModel) -> 'BDDProductDistribution': + bdd_model = cast(BDDModel, model) + self.result = product_distribution(bdd_model) return self def get_result(self) -> list[int]: return self.result def product_distribution(self) -> list[int]: - return product_distribution(self.bdd_model) + return self.get_result() def product_distribution(bdd_model: BDDModel) -> list[int]: @@ -113,4 +114,4 @@ def combine_distributions(id_node: int, node_dist[i] = value for i, value in enumerate(high_dist): node_dist[i + 1] = node_dist[i + 1] + value - dist[id_node] = node_dist \ No newline at end of file + dist[id_node] = node_dist diff --git a/flamapy/metamodels/bdd_metamodel/operations/bdd_products.py b/flamapy/metamodels/bdd_metamodel/operations/bdd_products.py index 23534a0..4596109 100644 --- a/flamapy/metamodels/bdd_metamodel/operations/bdd_products.py +++ b/flamapy/metamodels/bdd_metamodel/operations/bdd_products.py @@ -1,5 +1,6 @@ -from typing import Optional +from typing import Optional, cast +from flamapy.core.models import VariabilityModel from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration from flamapy.core.operations import Products from flamapy.metamodels.bdd_metamodel.models.bdd_model import BDDModel @@ -13,31 +14,30 @@ class BDDProducts(Products): def __init__(self, partial_configuration: Optional[Configuration] = None) -> None: self.result: list[Configuration] = [] - self.bdd_model = None self.partial_configuration = partial_configuration - def execute(self, model: BDDModel) -> 'BDDProducts': - self.bdd_model = model - self.result = products(self.bdd_model, self.partial_configuration) + def execute(self, model: VariabilityModel) -> 'BDDProducts': + bdd_model = cast(BDDModel, model) + self.result = products(bdd_model, self.partial_configuration) return self def get_result(self) -> list[Configuration]: return self.result def get_products(self) -> list[Configuration]: - return products(self.bdd_model, self.partial_configuration) + return self.get_result() def products(bdd_model: BDDModel, partial_config: Optional[Configuration] = None) -> list[Configuration]: if partial_config is None: u_func = bdd_model.root - care_vars = bdd_model.variables + care_vars = set(bdd_model.variables) elements = {} else: values = dict(partial_config.elements.items()) u_func = bdd_model.bdd.let(values, bdd_model.root) - care_vars = set(bdd_model.variables) - values.keys() + care_vars = set(bdd_model.variables) - set(values.keys()) elements = partial_config.elements configs = [] diff --git a/flamapy/metamodels/bdd_metamodel/operations/bdd_products_number.py b/flamapy/metamodels/bdd_metamodel/operations/bdd_products_number.py index fd39240..81a9707 100644 --- a/flamapy/metamodels/bdd_metamodel/operations/bdd_products_number.py +++ b/flamapy/metamodels/bdd_metamodel/operations/bdd_products_number.py @@ -1,5 +1,6 @@ -from typing import Optional +from typing import Optional, cast +from flamapy.core.models import VariabilityModel from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration from flamapy.core.operations import ProductsNumber from flamapy.metamodels.bdd_metamodel.models.bdd_model import BDDModel @@ -13,20 +14,18 @@ class BDDProductsNumber(ProductsNumber): def __init__(self, partial_configuration: Optional[Configuration] = None) -> None: self.result = 0 - self.bdd_model = None - self.feature_model = None self.partial_configuration = partial_configuration - def execute(self, model: BDDModel) -> 'BDDProductsNumber': - self.bdd_model = model - self.result = products_number(self.bdd_model, self.partial_configuration) + def execute(self, model: VariabilityModel) -> 'BDDProductsNumber': + bdd_model = cast(BDDModel, model) + self.result = products_number(bdd_model, self.partial_configuration) return self def get_result(self) -> int: return self.result def get_products_number(self) -> int: - return products_number(self.bdd_model, self.partial_configuration) + return self.get_result() def products_number(bdd_model: BDDModel, diff --git a/flamapy/metamodels/bdd_metamodel/operations/bdd_sampling.py b/flamapy/metamodels/bdd_metamodel/operations/bdd_sampling.py index 641d611..84e9de4 100644 --- a/flamapy/metamodels/bdd_metamodel/operations/bdd_sampling.py +++ b/flamapy/metamodels/bdd_metamodel/operations/bdd_sampling.py @@ -1,11 +1,11 @@ import random -from typing import Optional +from typing import Optional, cast -from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration +from flamapy.core.models import VariabilityModel +from flamapy.core.exceptions import FlamaException from flamapy.core.operations import Sampling - +from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration from flamapy.metamodels.bdd_metamodel.models import BDDModel -from flamapy.metamodels.bdd_metamodel.operations import BDDProductsNumber class BDDSampling(Sampling): @@ -21,43 +21,52 @@ class BDDSampling(Sampling): as well as samples from a given partial configuration. """ - def __init__(self, size: int, with_replacement: bool = False, - partial_configuration: Optional[Configuration] = None) -> None: + def __init__(self) -> None: self.result: list[Configuration] = [] - self.bdd_model = None - self.size = size + self.sample_size: int = 0 + self.with_replacement: bool = False + self.partial_configuration: Optional[Configuration] = None + + def set_sample_size(self, sample_size: int) -> None: + if sample_size < 0: + raise FlamaException(f'Sample size {sample_size} cannot be negative.') + self.sample_size = sample_size + + def set_with_replacement(self, with_replacement: bool) -> None: self.with_replacement = with_replacement + + def set_partial_configuration(self, partial_configuration: Configuration) -> None: self.partial_configuration = partial_configuration - def execute(self, model: BDDModel) -> 'BDDSampling': - self.bdd_model = model - self.result = sample(self.bdd_model, self.size, self.with_replacement, - self.partial_configuration) - return self + def get_sample(self) -> list[Configuration]: + return self.get_result() def get_result(self) -> list[Configuration]: return self.result - def sample(self, size: int, with_replacement: bool = False, - partial_configuration: Optional[Configuration] = None) -> list[Configuration]: - return sample(self.bdd_model, size, with_replacement, partial_configuration) - + def execute(self, model: VariabilityModel) -> 'BDDSampling': + bdd_model = cast(BDDModel, model) + self.result = sample(bdd_model, + self.sample_size, + self.with_replacement, + self.partial_configuration) + return self -def sample(bdd_model: BDDModel, size: int, with_replacement: bool = False, - partial_configuration: Optional[Configuration] = None) -> list[Configuration]: - nof_configs = BDDProductsNumber(partial_configuration).execute(bdd_model).get_result() - if size < 0 or (size > nof_configs and not with_replacement): - raise ValueError('Sample larger than population or is negative.') +def sample(model: BDDModel, + sample_size: int, + with_replacement: bool, + partial_configuration: Optional[Configuration] + ) -> list[Configuration]: configurations = [] - for _ in range(size): - config = random_configuration(bdd_model, partial_configuration) + for _ in range(sample_size): + config = random_configuration(model, partial_configuration) configurations.append(config) if not with_replacement: set_configurations = set(configurations) - while len(set_configurations) < size: - config = random_configuration(bdd_model, partial_configuration) + while len(set_configurations) < sample_size: + config = random_configuration(model, partial_configuration) set_configurations.add(config) return list(set_configurations) diff --git a/flamapy/metamodels/bdd_metamodel/transformations/sat_to_bdd.py b/flamapy/metamodels/bdd_metamodel/transformations/sat_to_bdd.py index ec11f46..a14abe4 100644 --- a/flamapy/metamodels/bdd_metamodel/transformations/sat_to_bdd.py +++ b/flamapy/metamodels/bdd_metamodel/transformations/sat_to_bdd.py @@ -41,8 +41,7 @@ def transform(self) -> BDDModel: ) cnf_formula = and_connective.join(cnf_list) - self.destination_model.from_textual_cnf( - cnf_formula, list(self.source_model.variables.keys()) - ) + self.destination_model.from_textual_cnf(cnf_formula, + list(self.source_model.variables.keys())) return self.destination_model diff --git a/setup.py b/setup.py index 92bcfea..78eeba6 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setuptools.setup( name="flamapy-bdd", - version="1.0.1", + version="1.6.0", author="Flamapy", author_email="flamapy@us.es", description="bdd-plugin for the automated analysis of feature models", diff --git a/tests/test_bdd_metamodel.py b/tests/test_bdd_metamodel.py index 29b7950..45bb2c3 100644 --- a/tests/test_bdd_metamodel.py +++ b/tests/test_bdd_metamodel.py @@ -20,7 +20,7 @@ def test_main(): # Create the BDD from the FM bdd_model = FmToBDD(feature_model).transform() - # # Save the BDD as a .png + # Save the BDD as a .png bdd_writer = BDDWriter(bdd_model.root.var + '.png', bdd_model) bdd_writer.set_format(BDDDumpFormat.SVG) bdd_writer.set_roots([bdd_model.root]) @@ -32,7 +32,7 @@ def test_main(): # BDD products operation products = BDDProducts().execute(bdd_model).get_result() - for i, prod in enumerate(products): + for i, prod in enumerate(products, 1): print(f'Product {i}: {[feat for feat in prod.elements if prod.elements[feat]]}') assert len(products) == nof_products @@ -50,9 +50,11 @@ def test_main(): print(f'{feat}: {prob[feat]}') # BDD Sampling - sample = BDDSampling(size=5, with_replacement=False).execute(bdd_model).get_result() + sample_op = BDDSampling() + sample_op.set_sample_size(5) + sample = sample_op.execute(bdd_model).get_result() print('Uniform Random Sampling:') - for i, prod in enumerate(sample): + for i, prod in enumerate(sample, 1): print(f'Product {i}: {[feat for feat in prod.elements if prod.elements[feat]]}')