Skip to content

Commit

Permalink
New pure optional features op and add partial configs for all operations
Browse files Browse the repository at this point in the history
  • Loading branch information
jmhorcas committed May 16, 2024
1 parent 50b65d2 commit 5c4e673
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 52 deletions.
2 changes: 2 additions & 0 deletions flamapy/metamodels/bdd_metamodel/operations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .bdd_satisfiable import BDDSatisfiable
from .bdd_core_features import BDDCoreFeatures
from .bdd_dead_features import BDDDeadFeatures
from .bdd_pure_optional_features import BDDPureOptionalFeatures
from .bdd_metrics import BDDMetrics


Expand All @@ -17,4 +18,5 @@
'BDDSatisfiable',
'BDDCoreFeatures',
'BDDDeadFeatures',
'BDDPureOptionalFeatures'
'BDDMetrics']
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ class BDDConfigurations(Configurations):
"""

def __init__(self) -> None:
self.result: list[Configuration] = []
self.partial_configuration: Optional[Configuration] = None
self._result: list[Configuration] = []
self._partial_configuration: Optional[Configuration] = None

def set_partial_configuration(self, partial_configuration: Configuration) -> None:
self.partial_configuration = partial_configuration
self._partial_configuration = partial_configuration

def execute(self, model: VariabilityModel) -> 'BDDConfigurations':
bdd_model = cast(BDDModel, model)
self.result = configurations(bdd_model, self.partial_configuration)
self._result = configurations(bdd_model, self._partial_configuration)
return self

def get_result(self) -> list[Configuration]:
return self.result
return self._result

def get_configurations(self) -> list[Configuration]:
return self.get_result()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ class BDDConfigurationsNumber(ConfigurationsNumber):
"""

def __init__(self) -> None:
self.result = 0
self.partial_configuration: Optional[Configuration] = None
self._result: int = 0
self._partial_configuration: Optional[Configuration] = None

def set_partial_configuration(self, partial_configuration: Optional[Configuration]) -> None:
self.partial_configuration = partial_configuration
self._partial_configuration = partial_configuration

def execute(self, model: VariabilityModel) -> 'BDDConfigurationsNumber':
bdd_model = cast(BDDModel, model)
self.result = configurations_number(bdd_model, self.partial_configuration)
self._result = configurations_number(bdd_model, self._partial_configuration)
return self

def get_result(self) -> int:
return self.result
return self._result

def get_configurations_number(self) -> int:
return self.get_result()
Expand Down
22 changes: 15 additions & 7 deletions flamapy/metamodels/bdd_metamodel/operations/bdd_core_features.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Any, cast
from typing import Any, Optional, cast

from flamapy.core.models import VariabilityModel
from flamapy.metamodels.configuration_metamodel.models import Configuration
from flamapy.core.operations import CoreFeatures
from flamapy.metamodels.bdd_metamodel.models import BDDModel
from flamapy.metamodels.bdd_metamodel.operations import BDDFeatureInclusionProbability
Expand All @@ -9,20 +10,27 @@
class BDDCoreFeatures(CoreFeatures):

def __init__(self) -> None:
self.result: list[Any] = []
self._result: list[Any] = []
self._partial_configuration: Optional[Configuration] = None

def set_partial_configuration(self, partial_configuration: Optional[Configuration]) -> None:
self._partial_configuration = partial_configuration

def execute(self, model: VariabilityModel) -> 'BDDCoreFeatures':
bdd_model = cast(BDDModel, model)
self.result = core_features(bdd_model)
self._result = core_features(bdd_model, self._partial_configuration)
return self

def get_result(self) -> list[Any]:
return self.result
return self._result

def get_core_features(self) -> list[Any]:
return self.get_result()


def core_features(bdd_model: BDDModel) -> list[Any]:
feat_incl_prob = BDDFeatureInclusionProbability().execute(bdd_model).get_result()
return [f for f, p in feat_incl_prob.items() if p >= 1.0]
def core_features(bdd_model: BDDModel,
config: Optional[Configuration] = None) -> list[Any]:
fip_op = BDDFeatureInclusionProbability()
fip_op.set_partial_configuration(config)
probs = fip_op.execute(bdd_model).get_result()
return [feat for feat, prob, in probs.items() if prob >= 1.0]
22 changes: 15 additions & 7 deletions flamapy/metamodels/bdd_metamodel/operations/bdd_dead_features.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Any, cast
from typing import Any, Optional, cast

from flamapy.core.models import VariabilityModel
from flamapy.metamodels.configuration_metamodel.models import Configuration
from flamapy.core.operations import DeadFeatures
from flamapy.metamodels.bdd_metamodel.models import BDDModel
from flamapy.metamodels.bdd_metamodel.operations import BDDFeatureInclusionProbability
Expand All @@ -9,20 +10,27 @@
class BDDDeadFeatures(DeadFeatures):

def __init__(self) -> None:
self.result: list[Any] = []
self._result: list[Any] = []
self._partial_configuration: Optional[Configuration] = None

def set_partial_configuration(self, partial_configuration: Optional[Configuration]) -> None:
self._partial_configuration = partial_configuration

def execute(self, model: VariabilityModel) -> 'BDDDeadFeatures':
bdd_model = cast(BDDModel, model)
self.result = dead_features(bdd_model)
self._result = dead_features(bdd_model, self._partial_configuration)
return self

def get_result(self) -> list[Any]:
return self.result
return self._result

def get_dead_features(self) -> list[Any]:
return self.get_result()


def dead_features(bdd_model: BDDModel) -> list[Any]:
feat_incl_prob = BDDFeatureInclusionProbability().execute(bdd_model).get_result()
return [f for f, p in feat_incl_prob.items() if p <= 0.0]
def dead_features(bdd_model: BDDModel,
config: Optional[Configuration] = None) -> list[Any]:
fip_op = BDDFeatureInclusionProbability()
fip_op.set_partial_configuration(config)
probs = fip_op.execute(bdd_model).get_result()
return [feat for feat, prob, in probs.items() if prob <= 0.0]
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,25 @@ class BDDFeatureInclusionProbability(FeatureInclusionProbability):
"""The Feature Inclusion Probability (FIP) operation determines the probability
for a variable to be included in a valid solution.
Ref.: [Heradio et al. 2019. Supporting the Statistical Analysis of Variability Models. SPLC.
Ref.: [Heradio et al. 2019. Supporting the Statistical Analysis of Variability Models.
(https://doi.org/10.1109/ICSE.2019.00091)]
"""

def __init__(self, partial_configuration: Optional[Configuration] = None) -> None:
self.result: dict[Any, float] = {}
self.partial_configuration = partial_configuration
def __init__(self) -> None:
self._result: dict[Any, float] = {}
self._partial_configuration: Optional[Configuration] = None

def set_partial_configuration(self, partial_configuration: Optional[Configuration]) -> None:
self._partial_configuration = partial_configuration

def execute(self, model: VariabilityModel) -> 'BDDFeatureInclusionProbability':
bdd_model = cast(BDDModel, model)
self.result = feature_inclusion_probability(bdd_model, self.partial_configuration)
self._result = feature_inclusion_probability(bdd_model, self._partial_configuration)
#self.result = variable_probabilities_single_traverse(bdd_model)
return self

def get_result(self) -> dict[Any, float]:
return self.result
return self._result

def feature_inclusion_probability(self) -> dict[Any, float]:
return self.get_result()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
class BDDProductDistribution(ProductDistribution):

def __init__(self) -> None:
self.result: list[int] = []
self._result: list[int] = []

def execute(self, model: VariabilityModel) -> 'BDDProductDistribution':
bdd_model = cast(BDDModel, model)
self.result = product_distribution(bdd_model)
self._result = product_distribution(bdd_model)
return self

def get_result(self) -> list[int]:
return self.result
return self._result

def product_distribution(self) -> list[int]:
return self.get_result()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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 PureOptionalFeatures
from flamapy.metamodels.bdd_metamodel.operations import BDDFeatureInclusionProbability


class BDDPureOptionalFeatures(PureOptionalFeatures):

def __init__(self) -> None:
self._result: list[Any] = []
self._partial_configuration: Optional[Configuration] = None

def set_partial_configuration(self, partial_configuration: Optional[Configuration]) -> None:
self._partial_configuration = partial_configuration

def execute(self, model: VariabilityModel) -> 'BDDPureOptionalFeatures':
bdd_model = cast(BDDModel, model)
self._result = pure_optional_features(bdd_model, self._partial_configuration)
return self

def get_result(self) -> list[Any]:
return self._result

def pure_optional_features(self) -> list[Any]:
return self.get_result()


def pure_optional_features(bdd_model: BDDModel,
config: Optional[Configuration] = None) -> list[Any]:
fip_op = BDDFeatureInclusionProbability()
fip_op.set_partial_configuration(config)
probs = fip_op.execute(bdd_model).get_result()
return [feat for feat, prob, in probs.items() if prob == 0.5]
28 changes: 14 additions & 14 deletions flamapy/metamodels/bdd_metamodel/operations/bdd_sampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,34 @@ class BDDSampling(Sampling):
"""

def __init__(self) -> None:
self.result: list[Configuration] = []
self.sample_size: int = 0
self.with_replacement: bool = False
self.partial_configuration: Optional[Configuration] = None
self._result: list[Configuration] = []
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
self._sample_size = sample_size

def set_with_replacement(self, with_replacement: bool) -> None:
self.with_replacement = with_replacement
self._with_replacement = with_replacement

def set_partial_configuration(self, partial_configuration: Configuration) -> None:
self.partial_configuration = partial_configuration
self._partial_configuration = partial_configuration

def get_result(self) -> list[Configuration]:
return self._result

def get_sample(self) -> list[Configuration]:
return self.get_result()

def get_result(self) -> list[Configuration]:
return self.result

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)
self._result = sample(bdd_model,
self._sample_size,
self._with_replacement,
self._partial_configuration)
return self


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ class BDDSatisfiable(Satisfiable):
"""

def __init__(self) -> None:
self.result: bool = False
self._result: bool = False

def get_result(self) -> bool:
return self.result
return self._result

def is_satisfiable(self) -> bool:
return self.get_result()

def execute(self, model: VariabilityModel) -> 'BDDSatisfiable':
bdd_model = cast(BDDModel, model)
self.result = is_satisfiable(bdd_model)
self._result = is_satisfiable(bdd_model)
return self


Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from .product_distribution import ProductDistribution
from .feature_inclusion_probability import FeatureInclusionProbability
from .pure_optional_features import PureOptionalFeatures


__all__ = ['ProductDistribution', 'FeatureInclusionProbability']
__all__ = ['ProductDistribution',
'FeatureInclusionProbability',
'PureOptionalFeatures']
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from abc import abstractmethod
from typing import Any

from flamapy.core.operations import Operation


class PureOptionalFeatures(Operation):
"""Pure optional features are those feature with 0.5 (50%) probability
of being selected in a valid configuration, that is, their selection is unconstrained.
Ref.: [Heradio et al. 2019. Supporting the Statistical Analysis of Variability Models.
(https://doi.org/10.1109/ICSE.2019.00091)]
"""

@abstractmethod
def __init__(self) -> None:
pass

@abstractmethod
def pure_optional_features(self) -> list[Any]:
pass
7 changes: 6 additions & 1 deletion tests/test_bdd_metamodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
BDDConfigurations,
BDDCoreFeatures,
BDDDeadFeatures,
BDDSatisfiable
BDDSatisfiable,
BDDPureOptionalFeatures
)


Expand Down Expand Up @@ -85,6 +86,10 @@ def analyze_bdd(bdd_model: BDDModel) -> None:
dead_features = BDDDeadFeatures().execute(bdd_model).get_result()
print(f'Dead features: {dead_features}')

# Dead features
pure_optional_features = BDDPureOptionalFeatures().execute(bdd_model).get_result()
print(f'Pure optional features: {pure_optional_features}')

# BDD Sampling
sampling_op = BDDSampling()
sampling_op.set_sample_size(5)
Expand Down

0 comments on commit 5c4e673

Please sign in to comment.