flamapy 2.5.0__tar.gz → 2.6.0.dev0__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-2.6.0.dev0/PKG-INFO +36 -0
- flamapy-2.6.0.dev0/README.md +13 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/flamapy/interfaces/python/flamapy_feature_model.py +296 -10
- flamapy-2.6.0.dev0/flamapy.egg-info/PKG-INFO +36 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/flamapy.egg-info/SOURCES.txt +1 -0
- flamapy-2.6.0.dev0/flamapy.egg-info/requires.txt +12 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/pyproject.toml +6 -5
- flamapy-2.6.0.dev0/tests/test_documentation_examples.py +356 -0
- flamapy-2.5.0/PKG-INFO +0 -104
- flamapy-2.5.0/README.md +0 -82
- flamapy-2.5.0/flamapy.egg-info/PKG-INFO +0 -104
- flamapy-2.5.0/flamapy.egg-info/requires.txt +0 -11
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/LICENSE +0 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/flamapy/commands/__init__.py +0 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/flamapy/interfaces/python/__init__.py +0 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/flamapy.egg-info/dependency_links.txt +0 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/flamapy.egg-info/entry_points.txt +0 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/flamapy.egg-info/top_level.txt +0 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/setup.cfg +0 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/setup.py +0 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/tests/test_basic_units.py +0 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/tests/test_cli_basic_units.py +0 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/tests/test_fama_suite_bdd.py +0 -0
- {flamapy-2.5.0 → flamapy-2.6.0.dev0}/tests/test_fama_suite_sat.py +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flamapy
|
|
3
|
+
Version: 2.6.0.dev0
|
|
4
|
+
Summary: Flamapy feature model is a distribution of the flama framework containing all plugins required to analyze feature models. It also offers a richer API and a complete command line interface and documentation.
|
|
5
|
+
Author-email: Flamapy <flamapy@us.es>
|
|
6
|
+
License-Expression: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/flamapy/flamapy-feature-model
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: flamapy-fw~=2.6.0.dev0
|
|
12
|
+
Requires-Dist: flamapy-fm~=2.6.0.dev0
|
|
13
|
+
Requires-Dist: flamapy-sat~=2.6.0.dev0
|
|
14
|
+
Requires-Dist: flamapy-bdd~=2.6.0.dev0
|
|
15
|
+
Requires-Dist: flamapy-z3~=2.6.0.dev0
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
19
|
+
Requires-Dist: prospector; extra == "dev"
|
|
20
|
+
Requires-Dist: mypy; extra == "dev"
|
|
21
|
+
Requires-Dist: coverage; extra == "dev"
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# flamapy
|
|
25
|
+
|
|
26
|
+
The flamapy feature-model distribution: a meta-package that installs the
|
|
27
|
+
framework and the feature-model analysis plugins, and provides the command-line
|
|
28
|
+
interface and the easy-to-use Python facade (`FLAMAFeatureModel`).
|
|
29
|
+
|
|
30
|
+
**Documentation:** https://docs.flamapy.org
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install flamapy
|
|
36
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# flamapy
|
|
2
|
+
|
|
3
|
+
The flamapy feature-model distribution: a meta-package that installs the
|
|
4
|
+
framework and the feature-model analysis plugins, and provides the command-line
|
|
5
|
+
interface and the easy-to-use Python facade (`FLAMAFeatureModel`).
|
|
6
|
+
|
|
7
|
+
**Documentation:** https://docs.flamapy.org
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install flamapy
|
|
13
|
+
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import List,
|
|
2
|
+
from typing import Any, Dict, Generator, List, Optional, Tuple, Union
|
|
3
3
|
from flamapy.core.discover import DiscoverMetamodels
|
|
4
4
|
from flamapy.metamodels.fm_metamodel.models import FeatureModel
|
|
5
5
|
from flamapy.core.exceptions import FlamaException
|
|
@@ -15,19 +15,12 @@ class FLAMAFeatureModel:
|
|
|
15
15
|
Any model in UVL, FaMaXML or FeatureIDE format are accepted
|
|
16
16
|
"""
|
|
17
17
|
self.model_path = model_path
|
|
18
|
-
"""
|
|
19
|
-
Creating the interface witht he flama framework
|
|
20
|
-
"""
|
|
21
18
|
self.discover_metamodel = DiscoverMetamodels()
|
|
22
|
-
"""
|
|
23
|
-
We save the model for later ussage
|
|
24
|
-
"""
|
|
25
19
|
self.fm_model = self._read(model_path)
|
|
26
|
-
"""
|
|
27
|
-
We create a empty sat model and a bdd model to avoid double transformations
|
|
28
|
-
"""
|
|
29
20
|
self.sat_model = None
|
|
30
21
|
self.bdd_model = None
|
|
22
|
+
self.z3_model = None
|
|
23
|
+
self.diagnosis_model = None
|
|
31
24
|
|
|
32
25
|
def _read(self, model_path: str) -> FeatureModel:
|
|
33
26
|
return self.discover_metamodel.use_transformation_t2m(model_path, "fm")
|
|
@@ -40,6 +33,16 @@ class FLAMAFeatureModel:
|
|
|
40
33
|
if self.bdd_model is None:
|
|
41
34
|
self.bdd_model = self.discover_metamodel.use_transformation_m2m(self.fm_model, "bdd")
|
|
42
35
|
|
|
36
|
+
def _transform_to_z3(self) -> None:
|
|
37
|
+
if self.z3_model is None:
|
|
38
|
+
self.z3_model = self.discover_metamodel.use_transformation_m2m(self.fm_model, "z3")
|
|
39
|
+
|
|
40
|
+
def _transform_to_diagnosis(self) -> None:
|
|
41
|
+
if self.diagnosis_model is None:
|
|
42
|
+
self.diagnosis_model = self.discover_metamodel.use_transformation_m2m(
|
|
43
|
+
self.fm_model, "pysat_diagnosis"
|
|
44
|
+
)
|
|
45
|
+
|
|
43
46
|
def atomic_sets(self) -> Union[None, List[List[Any]]]:
|
|
44
47
|
"""
|
|
45
48
|
This operation is used to find the atomic sets in a model:
|
|
@@ -379,3 +382,286 @@ class FLAMAFeatureModel:
|
|
|
379
382
|
except FlamaException as exception:
|
|
380
383
|
logger.error("Error: %s", exception)
|
|
381
384
|
return None
|
|
385
|
+
|
|
386
|
+
# FM-based operations
|
|
387
|
+
|
|
388
|
+
def metrics(self) -> Union[None, List[Dict[str, Any]]]:
|
|
389
|
+
"""
|
|
390
|
+
Returns a collection of structural metrics for the feature model, such as number of
|
|
391
|
+
features, constraints, relations, and complexity indicators.
|
|
392
|
+
"""
|
|
393
|
+
try:
|
|
394
|
+
return self.discover_metamodel.use_operation(
|
|
395
|
+
self.fm_model, "FMMetrics"
|
|
396
|
+
).get_result()
|
|
397
|
+
except FlamaException as exception:
|
|
398
|
+
logger.error("Error: %s", exception)
|
|
399
|
+
return None
|
|
400
|
+
|
|
401
|
+
def variation_points(self) -> Union[None, Dict[str, List[str]]]:
|
|
402
|
+
"""
|
|
403
|
+
Returns the variation points of the feature model. A variation point is a feature that
|
|
404
|
+
has at least one non-mandatory child, representing a decision point in the model.
|
|
405
|
+
The result maps each variation point name to the list of its variant feature names.
|
|
406
|
+
"""
|
|
407
|
+
try:
|
|
408
|
+
raw = self.discover_metamodel.use_operation(
|
|
409
|
+
self.fm_model, "FMVariationPoints"
|
|
410
|
+
).get_result()
|
|
411
|
+
return {vp.name: [v.name for v in variants] for vp, variants in raw.items()}
|
|
412
|
+
except FlamaException as exception:
|
|
413
|
+
logger.error("Error: %s", exception)
|
|
414
|
+
return None
|
|
415
|
+
|
|
416
|
+
# SAT-based operations
|
|
417
|
+
|
|
418
|
+
def backbone(self) -> Union[None, Dict[str, List[Any]]]:
|
|
419
|
+
"""
|
|
420
|
+
Returns the backbone of the feature model: the set of features that must always be
|
|
421
|
+
selected (core) and those that must never be selected (dead) across all valid
|
|
422
|
+
configurations, grouped under 'core' and 'dead' keys.
|
|
423
|
+
"""
|
|
424
|
+
try:
|
|
425
|
+
self._transform_to_sat()
|
|
426
|
+
return self.discover_metamodel.use_operation(
|
|
427
|
+
self.sat_model, "PySATBackbone"
|
|
428
|
+
).get_result()
|
|
429
|
+
except FlamaException as exception:
|
|
430
|
+
logger.error("Error: %s", exception)
|
|
431
|
+
return None
|
|
432
|
+
|
|
433
|
+
# BDD-based operations
|
|
434
|
+
|
|
435
|
+
def homogeneity(self) -> Union[None, float]:
|
|
436
|
+
"""
|
|
437
|
+
Measures how similar the products of the feature model are to each other. It is
|
|
438
|
+
computed as the average commonality factor across all features. A value of 1.0 means
|
|
439
|
+
all products are identical; lower values indicate more diversity.
|
|
440
|
+
"""
|
|
441
|
+
try:
|
|
442
|
+
self._transform_to_bdd()
|
|
443
|
+
return self.discover_metamodel.use_operation(
|
|
444
|
+
self.bdd_model, "BDDHomogeneity"
|
|
445
|
+
).get_result()
|
|
446
|
+
except FlamaException as exception:
|
|
447
|
+
logger.error("Error: %s", exception)
|
|
448
|
+
return None
|
|
449
|
+
|
|
450
|
+
def product_distribution(self) -> Union[None, List[int]]:
|
|
451
|
+
"""
|
|
452
|
+
Returns the distribution of products by number of activated features. The value at
|
|
453
|
+
index i is the count of valid configurations that have exactly i features selected.
|
|
454
|
+
"""
|
|
455
|
+
try:
|
|
456
|
+
self._transform_to_bdd()
|
|
457
|
+
return self.discover_metamodel.use_operation(
|
|
458
|
+
self.bdd_model, "BDDProductDistribution"
|
|
459
|
+
).get_result()
|
|
460
|
+
except FlamaException as exception:
|
|
461
|
+
logger.error("Error: %s", exception)
|
|
462
|
+
return None
|
|
463
|
+
|
|
464
|
+
def variability(self) -> Union[None, Tuple[float, float]]:
|
|
465
|
+
"""
|
|
466
|
+
Returns a tuple of (total_variability, partial_variability). Total variability is the
|
|
467
|
+
ratio of valid configurations to the theoretical maximum; partial variability is the
|
|
468
|
+
ratio relative to only the variant features.
|
|
469
|
+
"""
|
|
470
|
+
try:
|
|
471
|
+
self._transform_to_bdd()
|
|
472
|
+
return self.discover_metamodel.use_operation(
|
|
473
|
+
self.bdd_model, "BDDVariability"
|
|
474
|
+
).get_result()
|
|
475
|
+
except FlamaException as exception:
|
|
476
|
+
logger.error("Error: %s", exception)
|
|
477
|
+
return None
|
|
478
|
+
|
|
479
|
+
def variant_features(self) -> Union[None, List[str]]:
|
|
480
|
+
"""
|
|
481
|
+
Returns the features that are neither core nor dead — they appear in some but not all
|
|
482
|
+
valid configurations. These are the features that actually vary across products.
|
|
483
|
+
"""
|
|
484
|
+
try:
|
|
485
|
+
self._transform_to_bdd()
|
|
486
|
+
return self.discover_metamodel.use_operation(
|
|
487
|
+
self.bdd_model, "BDDVariantFeatures"
|
|
488
|
+
).get_result()
|
|
489
|
+
except FlamaException as exception:
|
|
490
|
+
logger.error("Error: %s", exception)
|
|
491
|
+
return None
|
|
492
|
+
|
|
493
|
+
def unique_features(self) -> Union[None, List[str]]:
|
|
494
|
+
"""
|
|
495
|
+
Returns features that appear in exactly one valid configuration. These features
|
|
496
|
+
uniquely identify a single product in the product line.
|
|
497
|
+
"""
|
|
498
|
+
try:
|
|
499
|
+
self._transform_to_bdd()
|
|
500
|
+
return self.discover_metamodel.use_operation(
|
|
501
|
+
self.bdd_model, "BDDUniqueFeatures"
|
|
502
|
+
).get_result()
|
|
503
|
+
except FlamaException as exception:
|
|
504
|
+
logger.error("Error: %s", exception)
|
|
505
|
+
return None
|
|
506
|
+
|
|
507
|
+
def pure_optional_features(self) -> Union[None, List[str]]:
|
|
508
|
+
"""
|
|
509
|
+
Returns features with a feature inclusion probability of exactly 0.5, meaning they
|
|
510
|
+
are selected in exactly half of the valid configurations. These are the most
|
|
511
|
+
unconstrained optional features.
|
|
512
|
+
"""
|
|
513
|
+
try:
|
|
514
|
+
self._transform_to_bdd()
|
|
515
|
+
return self.discover_metamodel.use_operation(
|
|
516
|
+
self.bdd_model, "BDDPureOptionalFeatures"
|
|
517
|
+
).get_result()
|
|
518
|
+
except FlamaException as exception:
|
|
519
|
+
logger.error("Error: %s", exception)
|
|
520
|
+
return None
|
|
521
|
+
|
|
522
|
+
def feature_inclusion_probability(self) -> Union[None, Dict[str, float]]:
|
|
523
|
+
"""
|
|
524
|
+
Returns a mapping from each feature name to its inclusion probability: the fraction
|
|
525
|
+
of valid configurations in which that feature is selected. Core features have
|
|
526
|
+
probability 1.0 and dead features have probability 0.0.
|
|
527
|
+
"""
|
|
528
|
+
try:
|
|
529
|
+
self._transform_to_bdd()
|
|
530
|
+
return self.discover_metamodel.use_operation(
|
|
531
|
+
self.bdd_model, "BDDFeatureInclusionProbability"
|
|
532
|
+
).get_result()
|
|
533
|
+
except FlamaException as exception:
|
|
534
|
+
logger.error("Error: %s", exception)
|
|
535
|
+
return None
|
|
536
|
+
|
|
537
|
+
def configurations_with_n_features(self, n: int) -> Union[None, List[Configuration]]:
|
|
538
|
+
"""
|
|
539
|
+
Returns all valid configurations that have exactly n features selected.
|
|
540
|
+
"""
|
|
541
|
+
try:
|
|
542
|
+
self._transform_to_bdd()
|
|
543
|
+
operation = self.discover_metamodel.get_operation(
|
|
544
|
+
self.bdd_model, "BDDConfigurationsWithNFeatures"
|
|
545
|
+
)
|
|
546
|
+
operation.set_n_features(n)
|
|
547
|
+
operation.execute(self.bdd_model)
|
|
548
|
+
return list(operation.get_result())
|
|
549
|
+
except FlamaException as exception:
|
|
550
|
+
logger.error("Error: %s", exception)
|
|
551
|
+
return None
|
|
552
|
+
|
|
553
|
+
def sampling(
|
|
554
|
+
self, size: int, with_replacement: bool = False
|
|
555
|
+
) -> Union[None, List[Configuration]]:
|
|
556
|
+
"""
|
|
557
|
+
Returns a random sample of valid configurations of the given size. When
|
|
558
|
+
with_replacement is True, the same configuration may appear more than once.
|
|
559
|
+
"""
|
|
560
|
+
try:
|
|
561
|
+
self._transform_to_bdd()
|
|
562
|
+
operation = self.discover_metamodel.get_operation(self.bdd_model, "BDDSampling")
|
|
563
|
+
operation.set_sample_size(size)
|
|
564
|
+
operation.set_with_replacement(with_replacement)
|
|
565
|
+
operation.execute(self.bdd_model)
|
|
566
|
+
return operation.get_result()
|
|
567
|
+
except FlamaException as exception:
|
|
568
|
+
logger.error("Error: %s", exception)
|
|
569
|
+
return None
|
|
570
|
+
|
|
571
|
+
# Z3-based operations
|
|
572
|
+
|
|
573
|
+
def all_feature_bounds(self) -> Union[None, Dict[str, Dict[str, Any]]]:
|
|
574
|
+
"""
|
|
575
|
+
Returns the value bounds for all typed (Integer, Real, String) attribute features in
|
|
576
|
+
the model. Each entry maps a feature name to a dict with 'min', 'max', and 'bounded'
|
|
577
|
+
keys. Requires the z3 metamodel plugin.
|
|
578
|
+
"""
|
|
579
|
+
try:
|
|
580
|
+
self._transform_to_z3()
|
|
581
|
+
return self.discover_metamodel.use_operation(
|
|
582
|
+
self.z3_model, "Z3AllFeatureBounds"
|
|
583
|
+
).get_result()
|
|
584
|
+
except FlamaException as exception:
|
|
585
|
+
logger.error("Error: %s", exception)
|
|
586
|
+
return None
|
|
587
|
+
|
|
588
|
+
def feature_bounds(self, variable_name: str) -> Union[None, Dict[str, Any]]:
|
|
589
|
+
"""
|
|
590
|
+
Returns the value bounds for a specific typed attribute feature. The result dict
|
|
591
|
+
contains 'min', 'max', and 'bounded' keys. Requires the z3 metamodel plugin.
|
|
592
|
+
"""
|
|
593
|
+
try:
|
|
594
|
+
self._transform_to_z3()
|
|
595
|
+
operation = self.discover_metamodel.get_operation(self.z3_model, "Z3FeatureBounds")
|
|
596
|
+
operation.set_variable_name(variable_name)
|
|
597
|
+
operation.execute(self.z3_model)
|
|
598
|
+
return operation.get_result()
|
|
599
|
+
except FlamaException as exception:
|
|
600
|
+
logger.error("Error: %s", exception)
|
|
601
|
+
return None
|
|
602
|
+
|
|
603
|
+
# Diagnosis-based operations
|
|
604
|
+
|
|
605
|
+
def diagnosis(
|
|
606
|
+
self,
|
|
607
|
+
configuration_path: str,
|
|
608
|
+
test_case_path: str,
|
|
609
|
+
max_diagnoses: Optional[int] = None,
|
|
610
|
+
) -> Union[None, List[str]]:
|
|
611
|
+
"""
|
|
612
|
+
Returns a list of diagnoses explaining why a given configuration does not satisfy
|
|
613
|
+
the test case constraints. Each diagnosis is a minimal set of constraints whose
|
|
614
|
+
removal would resolve the inconsistency. Requires the pysat_diagnosis plugin.
|
|
615
|
+
"""
|
|
616
|
+
try:
|
|
617
|
+
self._transform_to_diagnosis()
|
|
618
|
+
configuration = self.discover_metamodel.use_transformation_t2m(
|
|
619
|
+
configuration_path, "configuration"
|
|
620
|
+
)
|
|
621
|
+
test_case = self.discover_metamodel.use_transformation_t2m(
|
|
622
|
+
test_case_path, "configuration"
|
|
623
|
+
)
|
|
624
|
+
operation = self.discover_metamodel.get_operation(
|
|
625
|
+
self.diagnosis_model, "PySATDiagnosis"
|
|
626
|
+
)
|
|
627
|
+
operation.set_configuration(configuration)
|
|
628
|
+
operation.set_test_case(test_case)
|
|
629
|
+
if max_diagnoses is not None:
|
|
630
|
+
operation.set_max_diagnoses(max_diagnoses)
|
|
631
|
+
operation.execute(self.diagnosis_model)
|
|
632
|
+
return operation.get_result()
|
|
633
|
+
except FlamaException as exception:
|
|
634
|
+
logger.error("Error: %s", exception)
|
|
635
|
+
return None
|
|
636
|
+
|
|
637
|
+
def conflict(
|
|
638
|
+
self,
|
|
639
|
+
configuration_path: str,
|
|
640
|
+
test_case_path: str,
|
|
641
|
+
max_conflicts: Optional[int] = None,
|
|
642
|
+
) -> Union[None, List[str]]:
|
|
643
|
+
"""
|
|
644
|
+
Returns a list of conflict sets: minimal subsets of the model constraints that are
|
|
645
|
+
inconsistent with the given configuration and test case. Requires the pysat_diagnosis
|
|
646
|
+
plugin.
|
|
647
|
+
"""
|
|
648
|
+
try:
|
|
649
|
+
self._transform_to_diagnosis()
|
|
650
|
+
configuration = self.discover_metamodel.use_transformation_t2m(
|
|
651
|
+
configuration_path, "configuration"
|
|
652
|
+
)
|
|
653
|
+
test_case = self.discover_metamodel.use_transformation_t2m(
|
|
654
|
+
test_case_path, "configuration"
|
|
655
|
+
)
|
|
656
|
+
operation = self.discover_metamodel.get_operation(
|
|
657
|
+
self.diagnosis_model, "PySATConflict"
|
|
658
|
+
)
|
|
659
|
+
operation.set_configuration(configuration)
|
|
660
|
+
operation.set_test_case(test_case)
|
|
661
|
+
if max_conflicts is not None:
|
|
662
|
+
operation.set_max_conflicts(max_conflicts)
|
|
663
|
+
operation.execute(self.diagnosis_model)
|
|
664
|
+
return operation.get_result()
|
|
665
|
+
except FlamaException as exception:
|
|
666
|
+
logger.error("Error: %s", exception)
|
|
667
|
+
return None
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flamapy
|
|
3
|
+
Version: 2.6.0.dev0
|
|
4
|
+
Summary: Flamapy feature model is a distribution of the flama framework containing all plugins required to analyze feature models. It also offers a richer API and a complete command line interface and documentation.
|
|
5
|
+
Author-email: Flamapy <flamapy@us.es>
|
|
6
|
+
License-Expression: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/flamapy/flamapy-feature-model
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: flamapy-fw~=2.6.0.dev0
|
|
12
|
+
Requires-Dist: flamapy-fm~=2.6.0.dev0
|
|
13
|
+
Requires-Dist: flamapy-sat~=2.6.0.dev0
|
|
14
|
+
Requires-Dist: flamapy-bdd~=2.6.0.dev0
|
|
15
|
+
Requires-Dist: flamapy-z3~=2.6.0.dev0
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
19
|
+
Requires-Dist: prospector; extra == "dev"
|
|
20
|
+
Requires-Dist: mypy; extra == "dev"
|
|
21
|
+
Requires-Dist: coverage; extra == "dev"
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# flamapy
|
|
25
|
+
|
|
26
|
+
The flamapy feature-model distribution: a meta-package that installs the
|
|
27
|
+
framework and the feature-model analysis plugins, and provides the command-line
|
|
28
|
+
interface and the easy-to-use Python facade (`FLAMAFeatureModel`).
|
|
29
|
+
|
|
30
|
+
**Documentation:** https://docs.flamapy.org
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install flamapy
|
|
36
|
+
```
|
|
@@ -4,17 +4,18 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "flamapy"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.6.0.dev0"
|
|
8
8
|
description = "Flamapy feature model is a distribution of the flama framework containing all plugins required to analyze feature models. It also offers a richer API and a complete command line interface and documentation."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "GPL-3.0-or-later"
|
|
11
11
|
authors = [{ name = "Flamapy", email = "flamapy@us.es" }]
|
|
12
12
|
requires-python = ">=3.9"
|
|
13
13
|
dependencies = [
|
|
14
|
-
"flamapy-fw~=2.
|
|
15
|
-
"flamapy-fm~=2.
|
|
16
|
-
"flamapy-sat~=2.
|
|
17
|
-
"flamapy-bdd~=2.
|
|
14
|
+
"flamapy-fw~=2.6.0.dev0",
|
|
15
|
+
"flamapy-fm~=2.6.0.dev0",
|
|
16
|
+
"flamapy-sat~=2.6.0.dev0",
|
|
17
|
+
"flamapy-bdd~=2.6.0.dev0",
|
|
18
|
+
"flamapy-z3~=2.6.0.dev0",
|
|
18
19
|
]
|
|
19
20
|
|
|
20
21
|
[project.optional-dependencies]
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""Executable versions of every code example in the documentation.
|
|
2
|
+
|
|
3
|
+
Each operation page under ``flamapy_docs/_operations`` ships up to three runnable
|
|
4
|
+
snippets: the easy-to-use Python facade, the ``DiscoverMetamodels`` framework call
|
|
5
|
+
and the advanced (manual transformation) flow. The tests below mirror those
|
|
6
|
+
snippets so the documentation stays runnable and any regression in a documented
|
|
7
|
+
operation is caught here.
|
|
8
|
+
|
|
9
|
+
The Z3 operations run against a model with typed attributes; ``flamapy-z3`` is a
|
|
10
|
+
dependency of the ``flamapy`` distribution, so they are always exercised.
|
|
11
|
+
"""
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
from flamapy.core.discover import DiscoverMetamodels
|
|
15
|
+
from flamapy.interfaces.python.flamapy_feature_model import FLAMAFeatureModel
|
|
16
|
+
|
|
17
|
+
VALID_MODEL = "./resources/models/simple/valid_model.uvl"
|
|
18
|
+
VALID_CONFIG = "./resources/configurations/valid_configuration.csvconf"
|
|
19
|
+
# Feature model carrying typed attributes (Integer feature + Real attributes),
|
|
20
|
+
# required by the Z3 examples.
|
|
21
|
+
TYPED_MODEL = "./resources/models/simple/typed_attributes.uvl"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Python facade examples (flamapy_docs/_operations/*.md, "facade usage")
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
def test_facade_atomic_sets():
|
|
29
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
30
|
+
assert len(fm.atomic_sets()) == 17
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_facade_average_branching_factor():
|
|
34
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
35
|
+
assert fm.average_branching_factor() == 2.45
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_facade_count_leafs():
|
|
39
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
40
|
+
assert fm.count_leafs() == 17
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_facade_estimated_number_of_configurations():
|
|
44
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
45
|
+
assert fm.estimated_number_of_configurations() == 1904
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_facade_feature_ancestors():
|
|
49
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
50
|
+
assert fm.feature_ancestors("v74") == ["PHP", "Server", "eCommerce"]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_facade_leaf_features():
|
|
54
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
55
|
+
assert len(fm.leaf_features()) == 17
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_facade_max_depth():
|
|
59
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
60
|
+
assert fm.max_depth() == 4
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_facade_core_features():
|
|
64
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
65
|
+
assert len(fm.core_features()) == 12
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_facade_dead_features():
|
|
69
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
70
|
+
assert fm.dead_features() == []
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_facade_false_optional_features():
|
|
74
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
75
|
+
assert fm.false_optional_features() == []
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_facade_filter():
|
|
79
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
80
|
+
assert len(fm.filter(VALID_CONFIG)) == 68
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_facade_configurations_number():
|
|
84
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
85
|
+
assert fm.configurations_number() == 816
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_facade_configurations():
|
|
89
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
90
|
+
assert len(fm.configurations()) == 816
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_facade_commonality():
|
|
94
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
95
|
+
assert isinstance(fm.commonality(VALID_CONFIG), float)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_facade_satisfiable_configuration():
|
|
99
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
100
|
+
assert fm.satisfiable_configuration(VALID_CONFIG, False) is True
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_facade_satisfiable():
|
|
104
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
105
|
+
assert fm.satisfiable() is True
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_facade_metrics():
|
|
109
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
110
|
+
result = fm.metrics()
|
|
111
|
+
assert isinstance(result, list) and len(result) == 61
|
|
112
|
+
assert all("name" in entry and "result" in entry for entry in result)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_facade_variation_points():
|
|
116
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
117
|
+
result = fm.variation_points()
|
|
118
|
+
assert isinstance(result, dict) and len(result) == 7
|
|
119
|
+
# Keys and values are feature-name strings.
|
|
120
|
+
assert all(isinstance(k, str) for k in result)
|
|
121
|
+
assert all(isinstance(v, str) for variants in result.values() for v in variants)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_facade_backbone():
|
|
125
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
126
|
+
result = fm.backbone()
|
|
127
|
+
assert isinstance(result, dict)
|
|
128
|
+
assert set(result.keys()) == {"core", "dead"}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_facade_homogeneity():
|
|
132
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
133
|
+
result = fm.homogeneity()
|
|
134
|
+
assert isinstance(result, float) and 0.0 <= result <= 1.0
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def test_facade_product_distribution():
|
|
138
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
139
|
+
result = fm.product_distribution()
|
|
140
|
+
assert isinstance(result, list)
|
|
141
|
+
# The distribution sums to the total number of configurations.
|
|
142
|
+
assert sum(result) == 816
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_facade_variability():
|
|
146
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
147
|
+
result = fm.variability()
|
|
148
|
+
assert isinstance(result, tuple) and len(result) == 2
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_facade_variant_features():
|
|
152
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
153
|
+
assert len(fm.variant_features()) == 16
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_facade_unique_features():
|
|
157
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
158
|
+
assert fm.unique_features() == []
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def test_facade_pure_optional_features():
|
|
162
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
163
|
+
assert sorted(fm.pure_optional_features()) == ["ADVANCED", "BASIC", "CreditCard", "Mobile"]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def test_facade_feature_inclusion_probability():
|
|
167
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
168
|
+
result = fm.feature_inclusion_probability()
|
|
169
|
+
assert isinstance(result, dict)
|
|
170
|
+
assert all(0.0 <= p <= 1.0 for p in result.values())
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def test_facade_configurations_with_n_features():
|
|
174
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
175
|
+
assert isinstance(fm.configurations_with_n_features(5), list)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_facade_sampling():
|
|
179
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
180
|
+
assert len(fm.sampling(5)) == 5
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def test_facade_diagnosis():
|
|
184
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
185
|
+
assert isinstance(fm.diagnosis(VALID_CONFIG, VALID_CONFIG), list)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def test_facade_conflict():
|
|
189
|
+
fm = FLAMAFeatureModel(VALID_MODEL)
|
|
190
|
+
assert isinstance(fm.conflict(VALID_CONFIG, VALID_CONFIG), list)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# ---------------------------------------------------------------------------
|
|
194
|
+
# Framework examples (flamapy_docs/_operations/*.md, "framework usage")
|
|
195
|
+
# `dm.use_operation_from_file(...)` for operations that need no configuration.
|
|
196
|
+
# ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
FRAMEWORK_OPERATIONS = [
|
|
199
|
+
"AtomicSets", # base-class name resolves to FMAtomicSets
|
|
200
|
+
"FMAverageBranchingFactor",
|
|
201
|
+
"FMCountLeafs",
|
|
202
|
+
"FMEstimatedConfigurationsNumber",
|
|
203
|
+
"FMLeafFeatures",
|
|
204
|
+
"FMMaxDepthTree",
|
|
205
|
+
"FMMetrics",
|
|
206
|
+
"FMVariationPoints",
|
|
207
|
+
"PySATBackbone",
|
|
208
|
+
"PySATConfigurations",
|
|
209
|
+
"PySATConfigurationsNumber",
|
|
210
|
+
"PySATCoreFeatures",
|
|
211
|
+
"PySATDeadFeatures",
|
|
212
|
+
"PySATFalseOptionalFeatures",
|
|
213
|
+
"PySATSatisfiable",
|
|
214
|
+
"BDDFeatureInclusionProbability",
|
|
215
|
+
"BDDHomogeneity",
|
|
216
|
+
"BDDProductDistribution",
|
|
217
|
+
"BDDPureOptionalFeatures",
|
|
218
|
+
"BDDUniqueFeatures",
|
|
219
|
+
"BDDVariability",
|
|
220
|
+
"BDDVariantFeatures",
|
|
221
|
+
"Sampling", # base-class name resolves to a Sampling op
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@pytest.mark.parametrize("operation_name", FRAMEWORK_OPERATIONS)
|
|
226
|
+
def test_framework_use_operation_from_file(operation_name):
|
|
227
|
+
dm = DiscoverMetamodels()
|
|
228
|
+
result = dm.use_operation_from_file(operation_name, VALID_MODEL)
|
|
229
|
+
assert result is not None
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def test_framework_diagnosis_requires_configuration():
|
|
233
|
+
# Diagnosis/conflict are OperationWithConfiguration: they need configuration_file.
|
|
234
|
+
dm = DiscoverMetamodels()
|
|
235
|
+
result = dm.use_operation_from_file(
|
|
236
|
+
"PySATDiagnosis", VALID_MODEL, configuration_file=VALID_CONFIG
|
|
237
|
+
)
|
|
238
|
+
assert isinstance(result, list)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def test_framework_conflict_requires_configuration():
|
|
242
|
+
dm = DiscoverMetamodels()
|
|
243
|
+
result = dm.use_operation_from_file(
|
|
244
|
+
"PySATConflict", VALID_MODEL, configuration_file=VALID_CONFIG
|
|
245
|
+
)
|
|
246
|
+
assert isinstance(result, list)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
# ---------------------------------------------------------------------------
|
|
250
|
+
# Advanced framework examples (manual transformation + get_operation)
|
|
251
|
+
# ---------------------------------------------------------------------------
|
|
252
|
+
|
|
253
|
+
def test_advanced_dead_features():
|
|
254
|
+
dm = DiscoverMetamodels()
|
|
255
|
+
feature_model = dm.use_transformation_t2m(VALID_MODEL, "fm")
|
|
256
|
+
sat_model = dm.use_transformation_m2m(feature_model, "pysat")
|
|
257
|
+
operation = dm.get_operation(sat_model, "PySATDeadFeatures")
|
|
258
|
+
operation.execute(sat_model)
|
|
259
|
+
assert operation.get_result() == []
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def test_advanced_homogeneity():
|
|
263
|
+
dm = DiscoverMetamodels()
|
|
264
|
+
feature_model = dm.use_transformation_t2m(VALID_MODEL, "fm")
|
|
265
|
+
bdd_model = dm.use_transformation_m2m(feature_model, "bdd")
|
|
266
|
+
operation = dm.get_operation(bdd_model, "BDDHomogeneity")
|
|
267
|
+
operation.execute(bdd_model)
|
|
268
|
+
assert isinstance(operation.get_result(), float)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def test_advanced_configurations_with_n_features():
|
|
272
|
+
dm = DiscoverMetamodels()
|
|
273
|
+
feature_model = dm.use_transformation_t2m(VALID_MODEL, "fm")
|
|
274
|
+
bdd_model = dm.use_transformation_m2m(feature_model, "bdd")
|
|
275
|
+
operation = dm.get_operation(bdd_model, "BDDConfigurationsWithNFeatures")
|
|
276
|
+
operation.set_n_features(5)
|
|
277
|
+
operation.execute(bdd_model)
|
|
278
|
+
assert isinstance(list(operation.get_result()), list)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def test_advanced_diagnosis_with_configuration_and_test_case():
|
|
282
|
+
dm = DiscoverMetamodels()
|
|
283
|
+
feature_model = dm.use_transformation_t2m(VALID_MODEL, "fm")
|
|
284
|
+
sat_model = dm.use_transformation_m2m(feature_model, "pysat_diagnosis")
|
|
285
|
+
configuration = dm.use_transformation_t2m(VALID_CONFIG, "configuration")
|
|
286
|
+
test_case = dm.use_transformation_t2m(VALID_CONFIG, "configuration")
|
|
287
|
+
operation = dm.get_operation(sat_model, "PySATDiagnosis")
|
|
288
|
+
operation.set_configuration(configuration)
|
|
289
|
+
operation.set_test_case(test_case)
|
|
290
|
+
operation.execute(sat_model)
|
|
291
|
+
assert isinstance(operation.get_result(), list)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def test_advanced_conflict_with_configuration_and_test_case():
|
|
295
|
+
dm = DiscoverMetamodels()
|
|
296
|
+
feature_model = dm.use_transformation_t2m(VALID_MODEL, "fm")
|
|
297
|
+
sat_model = dm.use_transformation_m2m(feature_model, "pysat_diagnosis")
|
|
298
|
+
configuration = dm.use_transformation_t2m(VALID_CONFIG, "configuration")
|
|
299
|
+
test_case = dm.use_transformation_t2m(VALID_CONFIG, "configuration")
|
|
300
|
+
operation = dm.get_operation(sat_model, "PySATConflict")
|
|
301
|
+
operation.set_configuration(configuration)
|
|
302
|
+
operation.set_test_case(test_case)
|
|
303
|
+
operation.execute(sat_model)
|
|
304
|
+
assert isinstance(operation.get_result(), list)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# ---------------------------------------------------------------------------
|
|
308
|
+
# Z3 examples (optional plugin; require a model with typed attributes)
|
|
309
|
+
# ---------------------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
def test_facade_all_feature_bounds():
|
|
312
|
+
fm = FLAMAFeatureModel(TYPED_MODEL)
|
|
313
|
+
result = fm.all_feature_bounds()
|
|
314
|
+
assert result["Scoops"]["min"] == 1
|
|
315
|
+
assert result["Scoops"]["max"] == 5
|
|
316
|
+
assert result["Scoops"]["bounded"] is True
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def test_facade_feature_bounds():
|
|
320
|
+
fm = FLAMAFeatureModel(TYPED_MODEL)
|
|
321
|
+
result = fm.feature_bounds("Scoops")
|
|
322
|
+
assert result["min"] == 1
|
|
323
|
+
assert result["max"] == 5
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def test_advanced_all_feature_bounds():
|
|
327
|
+
dm = DiscoverMetamodels()
|
|
328
|
+
feature_model = dm.use_transformation_t2m(TYPED_MODEL, "fm")
|
|
329
|
+
z3_model = dm.use_transformation_m2m(feature_model, "z3")
|
|
330
|
+
operation = dm.get_operation(z3_model, "Z3AllFeatureBounds")
|
|
331
|
+
operation.execute(z3_model)
|
|
332
|
+
assert "Scoops" in operation.get_result()
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def test_advanced_variable_bounds():
|
|
336
|
+
dm = DiscoverMetamodels()
|
|
337
|
+
feature_model = dm.use_transformation_t2m(TYPED_MODEL, "fm")
|
|
338
|
+
z3_model = dm.use_transformation_m2m(feature_model, "z3")
|
|
339
|
+
operation = dm.get_operation(z3_model, "Z3VariableBounds")
|
|
340
|
+
operation.set_variable_name("Scoops")
|
|
341
|
+
operation.execute(z3_model)
|
|
342
|
+
result = operation.get_result()
|
|
343
|
+
assert result["min_bound"] == 1
|
|
344
|
+
assert result["max_bound"] == 5
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def test_advanced_attribute_optimization():
|
|
348
|
+
from flamapy.metamodels.z3_metamodel.operations.interfaces import OptimizationGoal
|
|
349
|
+
|
|
350
|
+
dm = DiscoverMetamodels()
|
|
351
|
+
feature_model = dm.use_transformation_t2m(TYPED_MODEL, "fm")
|
|
352
|
+
z3_model = dm.use_transformation_m2m(feature_model, "z3")
|
|
353
|
+
operation = dm.get_operation(z3_model, "Z3AttributeOptimization")
|
|
354
|
+
operation.set_attributes({"Price": OptimizationGoal.MINIMIZE})
|
|
355
|
+
operation.execute(z3_model)
|
|
356
|
+
assert isinstance(operation.get_result(), list)
|
flamapy-2.5.0/PKG-INFO
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: flamapy
|
|
3
|
-
Version: 2.5.0
|
|
4
|
-
Summary: Flamapy feature model is a distribution of the flama framework containing all plugins required to analyze feature models. It also offers a richer API and a complete command line interface and documentation.
|
|
5
|
-
Author-email: Flamapy <flamapy@us.es>
|
|
6
|
-
License-Expression: GPL-3.0-or-later
|
|
7
|
-
Project-URL: Homepage, https://github.com/flamapy/flamapy-feature-model
|
|
8
|
-
Requires-Python: >=3.9
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Requires-Dist: flamapy-fw~=2.5.0
|
|
12
|
-
Requires-Dist: flamapy-fm~=2.5.0
|
|
13
|
-
Requires-Dist: flamapy-sat~=2.5.0
|
|
14
|
-
Requires-Dist: flamapy-bdd~=2.5.0
|
|
15
|
-
Provides-Extra: dev
|
|
16
|
-
Requires-Dist: pytest; extra == "dev"
|
|
17
|
-
Requires-Dist: pytest-mock; extra == "dev"
|
|
18
|
-
Requires-Dist: prospector; extra == "dev"
|
|
19
|
-
Requires-Dist: mypy; extra == "dev"
|
|
20
|
-
Requires-Dist: coverage; extra == "dev"
|
|
21
|
-
Dynamic: license-file
|
|
22
|
-
|
|
23
|
-
<div align="center">
|
|
24
|
-
|
|
25
|
-
<a href="">[](https://github.com/flamapy/flamapy/actions/workflows/tests.yml)</a>
|
|
26
|
-
<a href="">[](https://github.com/flamapy/flamapy/actions/workflows/commits.yml)</a>
|
|
27
|
-
<a href="">
|
|
28
|
-
<a href="">
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
<div id="top"></div>
|
|
34
|
-
<br />
|
|
35
|
-
<div align="center">
|
|
36
|
-
|
|
37
|
-
<h3 align="center">FLAMAPY</h3>
|
|
38
|
-
|
|
39
|
-
<p align="center">
|
|
40
|
-
A new and easy way to use FLAMA
|
|
41
|
-
<br />
|
|
42
|
-
<a href="https://github.com/flamapy/flamapy/issues">Report Bug</a>
|
|
43
|
-
·
|
|
44
|
-
<a href="https://github.com/flamapy/flamapy/issues">Request Feature</a>
|
|
45
|
-
</p>
|
|
46
|
-
</div>
|
|
47
|
-
<!-- ABOUT THE PROJECT -->
|
|
48
|
-
|
|
49
|
-
# About The Project
|
|
50
|
-
FLAMAPY Feature model distribution provides an easier way of using FLAMAPY when analysing feature models. It packs the most used plugins for analyis of feature models adding a layer of convenience to use the framework or integrate it. If some other operations are required please see in the [documentation](flamapy.github.io/docs) if its supported in the ecosystem though the Python interface.
|
|
51
|
-
|
|
52
|
-
Feature Model Analysis has a crucial role in software product line engineering, enabling us to understand, design, and validate the complex relationships among features in a software product line. These feature models can often be complex and challenging to analyze due to their variability, making it difficult to identify conflicts, dead features, and potential optimizations. This is where this distribution comes in.
|
|
53
|
-
|
|
54
|
-
# Using the CMD
|
|
55
|
-
```bash
|
|
56
|
-
flamapy --help #This will show the commands available
|
|
57
|
-
flamapy satisfiable <path to file> to check if a model is valid
|
|
58
|
-
...
|
|
59
|
-
```
|
|
60
|
-
# Using the Python interface
|
|
61
|
-
This is simple, Flama FM dist in hosted in pypi, therefore simply add the package flama-fm-dist to your requirements file and call the API as follows:
|
|
62
|
-
|
|
63
|
-
```python
|
|
64
|
-
from flamapy.interfaces.python.flama_feature_model import FLAMAFeatureModel
|
|
65
|
-
|
|
66
|
-
fm = FLAMAFeatureModel("path/to/feature/model")
|
|
67
|
-
print(fm.valid())
|
|
68
|
-
```
|
|
69
|
-
# Operations available:
|
|
70
|
-
Currently this distribution offers a subset of the operations available in the ecosystem, in the case of the feature mdoel distribution, we provide those operations that are well tested and used by the community. Nonetheless, If other more complex operations are required you can rely on the python interface of the framework to execute them all.
|
|
71
|
-
|
|
72
|
-
* atomic_sets: This operation is used to find the atomic sets in a model: It returns the atomic sets if they are found in the model. If the model does not follow the UVL specification, an exception is raised and the operation returns False.
|
|
73
|
-
|
|
74
|
-
* average_branching_factor:This refers to the average number of child features that a parent feature has in a feature model. It's calculated by dividing the total number of child features by the total number of parent features. A high average branching factor indicates a complex feature model with many options, while a low average branching factor indicates a simpler model.
|
|
75
|
-
|
|
76
|
-
* commonality: This is a measure of how often a feature appears in the products of a product line. It's usually expressed as a percentage. A feature with 100 per cent commonality is a core feature, as it appears in all products.
|
|
77
|
-
|
|
78
|
-
* configurations: These are the individual outcomes that can be produced from a feature model. Each product is a combination of features that satisfies all the constraints and dependencies in the feature model.
|
|
79
|
-
|
|
80
|
-
* configurations_number: This is the total number of different full configurations that can be produced from a feature model. It's calculated by considering all possible combinations of features, taking into account the constraints and dependencies between features.
|
|
81
|
-
|
|
82
|
-
* core_features: These are the features that are present in all products of a product line. In a feature model, they are the features that are mandatory and not optional. Core features define the commonality among all products in a product line. This call requires sat to be called, however, there is an implementation within flamapy that does not requires sat, please use the framework in case of needing it.
|
|
83
|
-
|
|
84
|
-
* count_leafs: This operation counts the number of leaf features in a feature model. Leaf features are those that do not have any child features. They represent the most specific options in a product line.
|
|
85
|
-
* dead_features: These are features that, due to the constraints and dependencies in the feature model, cannot be included in any valid product. Dead features are usually a sign of an error in the feature model.
|
|
86
|
-
|
|
87
|
-
* estimated_number_of_configurations: This is an estimate of the total number of different products that can be produced from a feature model. It's calculated by considering all possible combinations of features. This can be a simple multiplication if all
|
|
88
|
-
features are independent, but in most cases, constraints and dependencies between features need to be taken into account.
|
|
89
|
-
|
|
90
|
-
* false_optional_features: These are features that appear to be optional in the feature model, but due to the constraints and dependencies, must be included in every valid product. Like dead features, false optional features are usually a sign of an error in the feature model.
|
|
91
|
-
|
|
92
|
-
* feature_ancestors: These are the features that are directly or indirectly the parent of a given feature in a feature model. Ancestors of a feature are found by traversing up the feature hierarchy. This information can be useful to understand the context and dependencies of a feature.
|
|
93
|
-
|
|
94
|
-
* filter: This operation selects a subset of the products of a product line based on certain criteria. For example, youmight filter the products to only include those that contain a certain feature.
|
|
95
|
-
|
|
96
|
-
* leaf_features: This operation is used to find leaf features in a model: It returns the leaf features if they are found in themodel. If the model does not follow the UVL specification, an exception is raised and the operation returns False.
|
|
97
|
-
|
|
98
|
-
* max_depth: This operation is used to find the max depth of the tree in a model: It returns the max depth of the tree. If the model does not follow the UVL specification, an exception is raised and the operation returns False.
|
|
99
|
-
|
|
100
|
-
* satisfiable: In the context of feature models, this usually refers to whether the feature model itself satisfies all the
|
|
101
|
-
constraints and dependencies. A a valid feature model is one that does encodes at least a single valid product.
|
|
102
|
-
|
|
103
|
-
* satisfiable_configuration: This is a product that is produced from a valid configuration of features. A valid product satisfies all the constraints and dependencies in the feature model.
|
|
104
|
-
|
flamapy-2.5.0/README.md
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
<a href="">[](https://github.com/flamapy/flamapy/actions/workflows/tests.yml)</a>
|
|
4
|
-
<a href="">[](https://github.com/flamapy/flamapy/actions/workflows/commits.yml)</a>
|
|
5
|
-
<a href="">
|
|
6
|
-
<a href="">
|
|
7
|
-
</div>
|
|
8
|
-
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
<div id="top"></div>
|
|
12
|
-
<br />
|
|
13
|
-
<div align="center">
|
|
14
|
-
|
|
15
|
-
<h3 align="center">FLAMAPY</h3>
|
|
16
|
-
|
|
17
|
-
<p align="center">
|
|
18
|
-
A new and easy way to use FLAMA
|
|
19
|
-
<br />
|
|
20
|
-
<a href="https://github.com/flamapy/flamapy/issues">Report Bug</a>
|
|
21
|
-
·
|
|
22
|
-
<a href="https://github.com/flamapy/flamapy/issues">Request Feature</a>
|
|
23
|
-
</p>
|
|
24
|
-
</div>
|
|
25
|
-
<!-- ABOUT THE PROJECT -->
|
|
26
|
-
|
|
27
|
-
# About The Project
|
|
28
|
-
FLAMAPY Feature model distribution provides an easier way of using FLAMAPY when analysing feature models. It packs the most used plugins for analyis of feature models adding a layer of convenience to use the framework or integrate it. If some other operations are required please see in the [documentation](flamapy.github.io/docs) if its supported in the ecosystem though the Python interface.
|
|
29
|
-
|
|
30
|
-
Feature Model Analysis has a crucial role in software product line engineering, enabling us to understand, design, and validate the complex relationships among features in a software product line. These feature models can often be complex and challenging to analyze due to their variability, making it difficult to identify conflicts, dead features, and potential optimizations. This is where this distribution comes in.
|
|
31
|
-
|
|
32
|
-
# Using the CMD
|
|
33
|
-
```bash
|
|
34
|
-
flamapy --help #This will show the commands available
|
|
35
|
-
flamapy satisfiable <path to file> to check if a model is valid
|
|
36
|
-
...
|
|
37
|
-
```
|
|
38
|
-
# Using the Python interface
|
|
39
|
-
This is simple, Flama FM dist in hosted in pypi, therefore simply add the package flama-fm-dist to your requirements file and call the API as follows:
|
|
40
|
-
|
|
41
|
-
```python
|
|
42
|
-
from flamapy.interfaces.python.flama_feature_model import FLAMAFeatureModel
|
|
43
|
-
|
|
44
|
-
fm = FLAMAFeatureModel("path/to/feature/model")
|
|
45
|
-
print(fm.valid())
|
|
46
|
-
```
|
|
47
|
-
# Operations available:
|
|
48
|
-
Currently this distribution offers a subset of the operations available in the ecosystem, in the case of the feature mdoel distribution, we provide those operations that are well tested and used by the community. Nonetheless, If other more complex operations are required you can rely on the python interface of the framework to execute them all.
|
|
49
|
-
|
|
50
|
-
* atomic_sets: This operation is used to find the atomic sets in a model: It returns the atomic sets if they are found in the model. If the model does not follow the UVL specification, an exception is raised and the operation returns False.
|
|
51
|
-
|
|
52
|
-
* average_branching_factor:This refers to the average number of child features that a parent feature has in a feature model. It's calculated by dividing the total number of child features by the total number of parent features. A high average branching factor indicates a complex feature model with many options, while a low average branching factor indicates a simpler model.
|
|
53
|
-
|
|
54
|
-
* commonality: This is a measure of how often a feature appears in the products of a product line. It's usually expressed as a percentage. A feature with 100 per cent commonality is a core feature, as it appears in all products.
|
|
55
|
-
|
|
56
|
-
* configurations: These are the individual outcomes that can be produced from a feature model. Each product is a combination of features that satisfies all the constraints and dependencies in the feature model.
|
|
57
|
-
|
|
58
|
-
* configurations_number: This is the total number of different full configurations that can be produced from a feature model. It's calculated by considering all possible combinations of features, taking into account the constraints and dependencies between features.
|
|
59
|
-
|
|
60
|
-
* core_features: These are the features that are present in all products of a product line. In a feature model, they are the features that are mandatory and not optional. Core features define the commonality among all products in a product line. This call requires sat to be called, however, there is an implementation within flamapy that does not requires sat, please use the framework in case of needing it.
|
|
61
|
-
|
|
62
|
-
* count_leafs: This operation counts the number of leaf features in a feature model. Leaf features are those that do not have any child features. They represent the most specific options in a product line.
|
|
63
|
-
* dead_features: These are features that, due to the constraints and dependencies in the feature model, cannot be included in any valid product. Dead features are usually a sign of an error in the feature model.
|
|
64
|
-
|
|
65
|
-
* estimated_number_of_configurations: This is an estimate of the total number of different products that can be produced from a feature model. It's calculated by considering all possible combinations of features. This can be a simple multiplication if all
|
|
66
|
-
features are independent, but in most cases, constraints and dependencies between features need to be taken into account.
|
|
67
|
-
|
|
68
|
-
* false_optional_features: These are features that appear to be optional in the feature model, but due to the constraints and dependencies, must be included in every valid product. Like dead features, false optional features are usually a sign of an error in the feature model.
|
|
69
|
-
|
|
70
|
-
* feature_ancestors: These are the features that are directly or indirectly the parent of a given feature in a feature model. Ancestors of a feature are found by traversing up the feature hierarchy. This information can be useful to understand the context and dependencies of a feature.
|
|
71
|
-
|
|
72
|
-
* filter: This operation selects a subset of the products of a product line based on certain criteria. For example, youmight filter the products to only include those that contain a certain feature.
|
|
73
|
-
|
|
74
|
-
* leaf_features: This operation is used to find leaf features in a model: It returns the leaf features if they are found in themodel. If the model does not follow the UVL specification, an exception is raised and the operation returns False.
|
|
75
|
-
|
|
76
|
-
* max_depth: This operation is used to find the max depth of the tree in a model: It returns the max depth of the tree. If the model does not follow the UVL specification, an exception is raised and the operation returns False.
|
|
77
|
-
|
|
78
|
-
* satisfiable: In the context of feature models, this usually refers to whether the feature model itself satisfies all the
|
|
79
|
-
constraints and dependencies. A a valid feature model is one that does encodes at least a single valid product.
|
|
80
|
-
|
|
81
|
-
* satisfiable_configuration: This is a product that is produced from a valid configuration of features. A valid product satisfies all the constraints and dependencies in the feature model.
|
|
82
|
-
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: flamapy
|
|
3
|
-
Version: 2.5.0
|
|
4
|
-
Summary: Flamapy feature model is a distribution of the flama framework containing all plugins required to analyze feature models. It also offers a richer API and a complete command line interface and documentation.
|
|
5
|
-
Author-email: Flamapy <flamapy@us.es>
|
|
6
|
-
License-Expression: GPL-3.0-or-later
|
|
7
|
-
Project-URL: Homepage, https://github.com/flamapy/flamapy-feature-model
|
|
8
|
-
Requires-Python: >=3.9
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Requires-Dist: flamapy-fw~=2.5.0
|
|
12
|
-
Requires-Dist: flamapy-fm~=2.5.0
|
|
13
|
-
Requires-Dist: flamapy-sat~=2.5.0
|
|
14
|
-
Requires-Dist: flamapy-bdd~=2.5.0
|
|
15
|
-
Provides-Extra: dev
|
|
16
|
-
Requires-Dist: pytest; extra == "dev"
|
|
17
|
-
Requires-Dist: pytest-mock; extra == "dev"
|
|
18
|
-
Requires-Dist: prospector; extra == "dev"
|
|
19
|
-
Requires-Dist: mypy; extra == "dev"
|
|
20
|
-
Requires-Dist: coverage; extra == "dev"
|
|
21
|
-
Dynamic: license-file
|
|
22
|
-
|
|
23
|
-
<div align="center">
|
|
24
|
-
|
|
25
|
-
<a href="">[](https://github.com/flamapy/flamapy/actions/workflows/tests.yml)</a>
|
|
26
|
-
<a href="">[](https://github.com/flamapy/flamapy/actions/workflows/commits.yml)</a>
|
|
27
|
-
<a href="">
|
|
28
|
-
<a href="">
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
<div id="top"></div>
|
|
34
|
-
<br />
|
|
35
|
-
<div align="center">
|
|
36
|
-
|
|
37
|
-
<h3 align="center">FLAMAPY</h3>
|
|
38
|
-
|
|
39
|
-
<p align="center">
|
|
40
|
-
A new and easy way to use FLAMA
|
|
41
|
-
<br />
|
|
42
|
-
<a href="https://github.com/flamapy/flamapy/issues">Report Bug</a>
|
|
43
|
-
·
|
|
44
|
-
<a href="https://github.com/flamapy/flamapy/issues">Request Feature</a>
|
|
45
|
-
</p>
|
|
46
|
-
</div>
|
|
47
|
-
<!-- ABOUT THE PROJECT -->
|
|
48
|
-
|
|
49
|
-
# About The Project
|
|
50
|
-
FLAMAPY Feature model distribution provides an easier way of using FLAMAPY when analysing feature models. It packs the most used plugins for analyis of feature models adding a layer of convenience to use the framework or integrate it. If some other operations are required please see in the [documentation](flamapy.github.io/docs) if its supported in the ecosystem though the Python interface.
|
|
51
|
-
|
|
52
|
-
Feature Model Analysis has a crucial role in software product line engineering, enabling us to understand, design, and validate the complex relationships among features in a software product line. These feature models can often be complex and challenging to analyze due to their variability, making it difficult to identify conflicts, dead features, and potential optimizations. This is where this distribution comes in.
|
|
53
|
-
|
|
54
|
-
# Using the CMD
|
|
55
|
-
```bash
|
|
56
|
-
flamapy --help #This will show the commands available
|
|
57
|
-
flamapy satisfiable <path to file> to check if a model is valid
|
|
58
|
-
...
|
|
59
|
-
```
|
|
60
|
-
# Using the Python interface
|
|
61
|
-
This is simple, Flama FM dist in hosted in pypi, therefore simply add the package flama-fm-dist to your requirements file and call the API as follows:
|
|
62
|
-
|
|
63
|
-
```python
|
|
64
|
-
from flamapy.interfaces.python.flama_feature_model import FLAMAFeatureModel
|
|
65
|
-
|
|
66
|
-
fm = FLAMAFeatureModel("path/to/feature/model")
|
|
67
|
-
print(fm.valid())
|
|
68
|
-
```
|
|
69
|
-
# Operations available:
|
|
70
|
-
Currently this distribution offers a subset of the operations available in the ecosystem, in the case of the feature mdoel distribution, we provide those operations that are well tested and used by the community. Nonetheless, If other more complex operations are required you can rely on the python interface of the framework to execute them all.
|
|
71
|
-
|
|
72
|
-
* atomic_sets: This operation is used to find the atomic sets in a model: It returns the atomic sets if they are found in the model. If the model does not follow the UVL specification, an exception is raised and the operation returns False.
|
|
73
|
-
|
|
74
|
-
* average_branching_factor:This refers to the average number of child features that a parent feature has in a feature model. It's calculated by dividing the total number of child features by the total number of parent features. A high average branching factor indicates a complex feature model with many options, while a low average branching factor indicates a simpler model.
|
|
75
|
-
|
|
76
|
-
* commonality: This is a measure of how often a feature appears in the products of a product line. It's usually expressed as a percentage. A feature with 100 per cent commonality is a core feature, as it appears in all products.
|
|
77
|
-
|
|
78
|
-
* configurations: These are the individual outcomes that can be produced from a feature model. Each product is a combination of features that satisfies all the constraints and dependencies in the feature model.
|
|
79
|
-
|
|
80
|
-
* configurations_number: This is the total number of different full configurations that can be produced from a feature model. It's calculated by considering all possible combinations of features, taking into account the constraints and dependencies between features.
|
|
81
|
-
|
|
82
|
-
* core_features: These are the features that are present in all products of a product line. In a feature model, they are the features that are mandatory and not optional. Core features define the commonality among all products in a product line. This call requires sat to be called, however, there is an implementation within flamapy that does not requires sat, please use the framework in case of needing it.
|
|
83
|
-
|
|
84
|
-
* count_leafs: This operation counts the number of leaf features in a feature model. Leaf features are those that do not have any child features. They represent the most specific options in a product line.
|
|
85
|
-
* dead_features: These are features that, due to the constraints and dependencies in the feature model, cannot be included in any valid product. Dead features are usually a sign of an error in the feature model.
|
|
86
|
-
|
|
87
|
-
* estimated_number_of_configurations: This is an estimate of the total number of different products that can be produced from a feature model. It's calculated by considering all possible combinations of features. This can be a simple multiplication if all
|
|
88
|
-
features are independent, but in most cases, constraints and dependencies between features need to be taken into account.
|
|
89
|
-
|
|
90
|
-
* false_optional_features: These are features that appear to be optional in the feature model, but due to the constraints and dependencies, must be included in every valid product. Like dead features, false optional features are usually a sign of an error in the feature model.
|
|
91
|
-
|
|
92
|
-
* feature_ancestors: These are the features that are directly or indirectly the parent of a given feature in a feature model. Ancestors of a feature are found by traversing up the feature hierarchy. This information can be useful to understand the context and dependencies of a feature.
|
|
93
|
-
|
|
94
|
-
* filter: This operation selects a subset of the products of a product line based on certain criteria. For example, youmight filter the products to only include those that contain a certain feature.
|
|
95
|
-
|
|
96
|
-
* leaf_features: This operation is used to find leaf features in a model: It returns the leaf features if they are found in themodel. If the model does not follow the UVL specification, an exception is raised and the operation returns False.
|
|
97
|
-
|
|
98
|
-
* max_depth: This operation is used to find the max depth of the tree in a model: It returns the max depth of the tree. If the model does not follow the UVL specification, an exception is raised and the operation returns False.
|
|
99
|
-
|
|
100
|
-
* satisfiable: In the context of feature models, this usually refers to whether the feature model itself satisfies all the
|
|
101
|
-
constraints and dependencies. A a valid feature model is one that does encodes at least a single valid product.
|
|
102
|
-
|
|
103
|
-
* satisfiable_configuration: This is a product that is produced from a valid configuration of features. A valid product satisfies all the constraints and dependencies in the feature model.
|
|
104
|
-
|
|
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
|