flamapy 2.5.0.dev0__py3-none-any.whl → 2.6.0.dev1__py3-none-any.whl

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.
@@ -5,13 +5,13 @@ from functools import wraps
5
5
  import inspect
6
6
  from pathlib import Path
7
7
  from shutil import copytree
8
- from typing import List, Tuple, Optional
8
+ from typing import Any, List, Tuple, Optional
9
9
  from types import FunctionType
10
10
 
11
11
  from flamapy.interfaces.python.flamapy_feature_model import FLAMAFeatureModel
12
12
 
13
13
  # List to store registered commands and their arguments
14
- MANUAL_COMMANDS = []
14
+ MANUAL_COMMANDS: List[Tuple[str, str, FunctionType, Tuple[Any, ...]]] = []
15
15
 
16
16
 
17
17
  def command(name, description, *args): # type: ignore
@@ -1,6 +1,8 @@
1
1
  import logging
2
- from typing import List, Any, Union
2
+ from enum import Enum
3
+ from typing import Any, Dict, List, Optional, Tuple, Union
3
4
  from flamapy.core.discover import DiscoverMetamodels
5
+ from flamapy.core.models import VariabilityModel
4
6
  from flamapy.metamodels.fm_metamodel.models import FeatureModel
5
7
  from flamapy.core.exceptions import FlamaException
6
8
  from flamapy.metamodels.configuration_metamodel.models import Configuration
@@ -8,30 +10,68 @@ from flamapy.metamodels.configuration_metamodel.models import Configuration
8
10
  logger = logging.getLogger(__name__)
9
11
 
10
12
 
13
+ class Backend(str, Enum):
14
+ """Analysis backend (plugin) used to run a facade operation."""
15
+
16
+ SAT = "sat"
17
+ BDD = "bdd"
18
+ Z3 = "z3"
19
+
20
+
11
21
  class FLAMAFeatureModel:
12
- def __init__(self, model_path: str):
22
+ # Backend -> (lazy transformation method, attribute caching the model)
23
+ _BACKENDS = {
24
+ Backend.SAT: ("_transform_to_sat", "sat_model"),
25
+ Backend.BDD: ("_transform_to_bdd", "bdd_model"),
26
+ Backend.Z3: ("_transform_to_z3", "z3_model"),
27
+ }
28
+
29
+ def __init__(self, model_path: str, backend: Optional[str] = None):
13
30
  """
14
31
  This is the path in the filesystem where the model is located.
15
- Any model in UVL, FaMaXML or FeatureIDE format are accepted
32
+ Any model in UVL, FaMaXML or FeatureIDE format are accepted.
33
+
34
+ ``backend`` optionally sets a workspace-wide default backend
35
+ ("sat", "bdd" or "z3") for every operation that supports more than
36
+ one. When left as ``None`` each operation keeps its own historical
37
+ default, so existing code behaves exactly as before.
16
38
  """
17
39
  self.model_path = model_path
18
- """
19
- Creating the interface witht he flama framework
20
- """
21
40
  self.discover_metamodel = DiscoverMetamodels()
22
- """
23
- We save the model for later ussage
24
- """
25
41
  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
42
  self.sat_model = None
30
43
  self.bdd_model = None
44
+ self.z3_model = None
45
+ self.diagnosis_model = None
46
+ self.backend = Backend(backend) if backend is not None else None
31
47
 
32
48
  def _read(self, model_path: str) -> FeatureModel:
33
49
  return self.discover_metamodel.use_transformation_t2m(model_path, "fm")
34
50
 
51
+ def _select_backend(
52
+ self,
53
+ backend: Optional[str],
54
+ default: Backend,
55
+ ) -> VariabilityModel:
56
+ """Resolve the backend to use, lazily transform to it and return its model.
57
+
58
+ Resolution order: explicit per-call ``backend`` > workspace-wide
59
+ ``self.backend`` > the operation's historical ``default``. All
60
+ backends (sat, bdd, z3) are accepted; if the chosen one does not
61
+ implement the requested operation a FlamaException is raised when it
62
+ runs.
63
+ """
64
+ try:
65
+ chosen = Backend(backend) if backend is not None else (self.backend or default)
66
+ except ValueError:
67
+ raise FlamaException(
68
+ f"Unknown backend '{backend}'. "
69
+ f"Choose from {[b.value for b in Backend]}."
70
+ )
71
+ transform, attr = self._BACKENDS[chosen]
72
+ getattr(self, transform)()
73
+ return getattr(self, attr)
74
+
35
75
  def _transform_to_sat(self) -> None:
36
76
  if self.sat_model is None:
37
77
  self.sat_model = self.discover_metamodel.use_transformation_m2m(self.fm_model, "pysat")
@@ -40,6 +80,16 @@ class FLAMAFeatureModel:
40
80
  if self.bdd_model is None:
41
81
  self.bdd_model = self.discover_metamodel.use_transformation_m2m(self.fm_model, "bdd")
42
82
 
83
+ def _transform_to_z3(self) -> None:
84
+ if self.z3_model is None:
85
+ self.z3_model = self.discover_metamodel.use_transformation_m2m(self.fm_model, "z3")
86
+
87
+ def _transform_to_diagnosis(self) -> None:
88
+ if self.diagnosis_model is None:
89
+ self.diagnosis_model = self.discover_metamodel.use_transformation_m2m(
90
+ self.fm_model, "pysat_diagnosis"
91
+ )
92
+
43
93
  def atomic_sets(self) -> Union[None, List[List[Any]]]:
44
94
  """
45
95
  This operation is used to find the atomic sets in a model:
@@ -195,55 +245,49 @@ class FLAMAFeatureModel:
195
245
  return None
196
246
 
197
247
  # The methods above rely on sat to be executed.
198
- def core_features(self) -> Union[None, List[str]]:
248
+ def core_features(self, backend: Optional[str] = None) -> Union[None, List[str]]:
199
249
  """
200
250
  These are the features that are present in all products of a product line.
201
251
  In a feature model, they are the features that are mandatory and not optional.
202
252
  Core features define the commonality among all products in a product line.
203
- This call requires sat to be called, however, there is an implementation within
204
- flamapy that does not requires sat. please use the framework in case of needing it.
253
+
254
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to sat.
205
255
  """
206
256
  try:
207
- self._transform_to_sat()
208
- features = self.discover_metamodel.use_operation(
209
- self.sat_model, "PySATCoreFeatures"
210
- ).get_result()
211
- return features
257
+ model = self._select_backend(backend, Backend.SAT)
258
+ return self.discover_metamodel.use_operation(model, "CoreFeatures").get_result()
212
259
  except FlamaException as exception:
213
260
  logger.error("Error: %s", exception)
214
261
  return None
215
262
 
216
- def dead_features(self) -> Union[None, List[str]]:
263
+ def dead_features(self, backend: Optional[str] = None) -> Union[None, List[str]]:
217
264
  """
218
265
  These are features that, due to the constraints and dependencies in the
219
266
  feature model, cannot be included in any valid product. Dead features are usually
220
267
  a sign of an error in the feature model.
268
+
269
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to sat.
221
270
  """
222
271
  try:
223
- self._transform_to_sat()
224
- features = self.discover_metamodel.use_operation(
225
- self.sat_model, "PySATDeadFeatures"
226
- ).get_result()
227
- return features
272
+ model = self._select_backend(backend, Backend.SAT)
273
+ return self.discover_metamodel.use_operation(model, "DeadFeatures").get_result()
228
274
  except FlamaException as exception:
229
275
  logger.error("Error: %s", exception)
230
276
  return None
231
277
 
232
- def false_optional_features(self) -> Union[None, List[str]]:
278
+ def false_optional_features(self, backend: Optional[str] = None) -> Union[None, List[str]]:
233
279
  """
234
280
  These are features that appear to be optional in the feature model, but due to the
235
281
  constraints and dependencies, must be included in every valid product. Like dead features,
236
282
  false optional features are usually a sign of an error in the feature model.
283
+
284
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to sat.
237
285
  """
238
286
  try:
239
- self._transform_to_sat()
240
- operation = self.discover_metamodel.get_operation(
241
- self.sat_model, "PySATFalseOptionalFeatures"
242
- )
243
- operation.feature_model = self.fm_model
244
- operation.execute(self.sat_model)
245
- features = operation.get_result()
246
- return features
287
+ model = self._select_backend(backend, Backend.SAT)
288
+ return self.discover_metamodel.use_operation(
289
+ model, "FalseOptionalFeatures"
290
+ ).get_result()
247
291
  except FlamaException as exception:
248
292
  logger.error("Error: %s", exception)
249
293
  return None
@@ -268,49 +312,35 @@ class FLAMAFeatureModel:
268
312
  logger.error("Error: %s", exception)
269
313
  return None
270
314
 
271
- def configurations_number(self, with_sat: bool = False) -> Union[None, int]:
315
+ def configurations_number(self, backend: Optional[str] = None) -> Union[None, int]:
272
316
  """
273
317
  This is the total number of different full configurations that can be
274
318
  produced from a feature model. It's calculated by considering all possible
275
319
  combinations of features, taking into account the constraints and
276
320
  dependencies between features.
321
+
322
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to bdd.
277
323
  """
278
324
  try:
279
- nop = 0
280
- if with_sat:
281
- self._transform_to_sat()
282
- nop = self.discover_metamodel.use_operation(
283
- self.sat_model, "PySATConfigurationsNumber"
284
- ).get_result()
285
- else:
286
- self._transform_to_bdd()
287
- nop = self.discover_metamodel.use_operation(
288
- self.bdd_model, "BDDConfigurationsNumber"
289
- ).get_result()
290
- return nop
325
+ model = self._select_backend(backend, Backend.BDD)
326
+ return self.discover_metamodel.use_operation(
327
+ model, "ConfigurationsNumber"
328
+ ).get_result()
291
329
  except FlamaException as exception:
292
330
  logger.error("Error: %s", exception)
293
331
  return None
294
332
 
295
- def configurations(self, with_sat: bool = False) -> Union[None, List[Configuration]]:
333
+ def configurations(self, backend: Optional[str] = None) -> Union[None, List[Configuration]]:
296
334
  """
297
335
  These are the individual outcomes that can be produced from a feature model. Each product
298
336
  is a combination of features that satisfies all the constraints and dependencies in the
299
337
  feature model.
338
+
339
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to bdd.
300
340
  """
301
341
  try:
302
- products = []
303
- if with_sat:
304
- self._transform_to_sat()
305
- products = self.discover_metamodel.use_operation(
306
- self.sat_model, "PySATConfigurations"
307
- ).get_result()
308
- else:
309
- self._transform_to_bdd()
310
- products = self.discover_metamodel.use_operation(
311
- self.bdd_model, "BDDConfigurations"
312
- ).get_result()
313
- return products
342
+ model = self._select_backend(backend, Backend.BDD)
343
+ return self.discover_metamodel.use_operation(model, "Configurations").get_result()
314
344
  except FlamaException as exception:
315
345
  logger.error("Error: %s", exception)
316
346
  return None
@@ -336,46 +366,332 @@ class FLAMAFeatureModel:
336
366
  return None
337
367
 
338
368
  def satisfiable_configuration(
339
- self, configuration_path: str, full_configuration: bool = False
369
+ self,
370
+ configuration_path: str,
371
+ full_configuration: bool = False,
372
+ backend: Optional[str] = None,
340
373
  ) -> Union[None, bool]:
341
374
  """
342
375
  This is a product that is produced from a valid configuration of features. A valid
343
376
  product satisfies all the constraints and dependencies in the feature model.
377
+
378
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to sat.
344
379
  """
345
380
  try:
346
- self._transform_to_sat()
381
+ model = self._select_backend(backend, Backend.SAT)
347
382
  configuration = self.discover_metamodel.use_transformation_t2m(
348
383
  configuration_path, "configuration"
349
384
  )
350
385
  operation = self.discover_metamodel.get_operation(
351
- self.sat_model, "PySATSatisfiableConfiguration"
386
+ model, "SatisfiableConfiguration"
352
387
  )
353
388
 
354
- if full_configuration:
355
- configuration.is_full = True
356
- else:
357
- configuration.is_full = False
389
+ configuration.is_full = bool(full_configuration)
358
390
 
359
391
  operation.set_configuration(configuration)
360
- operation.execute(self.sat_model)
361
- result = operation.get_result()
362
- return result
392
+ operation.execute(model)
393
+ return operation.get_result()
363
394
  except FlamaException as exception:
364
395
  logger.error("Error: %s", exception)
365
396
  return None
366
397
 
367
- def satisfiable(self) -> Union[None, bool]:
398
+ def satisfiable(self, backend: Optional[str] = None) -> Union[None, bool]:
368
399
  """
369
400
  In the context of feature models, this usually refers to whether the feature model itself
370
401
  satisfies all the constraints and dependencies. A a valid feature model is one that
371
402
  does encodes at least a single valid product.
403
+
404
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to sat.
405
+ """
406
+ try:
407
+ model = self._select_backend(backend, Backend.SAT)
408
+ return self.discover_metamodel.use_operation(model, "Satisfiable").get_result()
409
+ except FlamaException as exception:
410
+ logger.error("Error: %s", exception)
411
+ return None
412
+
413
+ # FM-based operations
414
+
415
+ def metrics(self) -> Union[None, List[Dict[str, Any]]]:
416
+ """
417
+ Returns a collection of structural metrics for the feature model, such as number of
418
+ features, constraints, relations, and complexity indicators.
419
+ """
420
+ try:
421
+ return self.discover_metamodel.use_operation(
422
+ self.fm_model, "FMMetrics"
423
+ ).get_result()
424
+ except FlamaException as exception:
425
+ logger.error("Error: %s", exception)
426
+ return None
427
+
428
+ def variation_points(self) -> Union[None, Dict[str, List[str]]]:
429
+ """
430
+ Returns the variation points of the feature model. A variation point is a feature that
431
+ has at least one non-mandatory child, representing a decision point in the model.
432
+ The result maps each variation point name to the list of its variant feature names.
433
+ """
434
+ try:
435
+ raw = self.discover_metamodel.use_operation(
436
+ self.fm_model, "FMVariationPoints"
437
+ ).get_result()
438
+ return {vp.name: [v.name for v in variants] for vp, variants in raw.items()}
439
+ except FlamaException as exception:
440
+ logger.error("Error: %s", exception)
441
+ return None
442
+
443
+ # SAT-based operations
444
+
445
+ def backbone(self) -> Union[None, Dict[str, List[Any]]]:
446
+ """
447
+ Returns the backbone of the feature model: the set of features that must always be
448
+ selected (core) and those that must never be selected (dead) across all valid
449
+ configurations, grouped under 'core' and 'dead' keys.
372
450
  """
373
451
  try:
374
452
  self._transform_to_sat()
375
- result = self.discover_metamodel.use_operation(
376
- self.sat_model, "PySATSatisfiable"
453
+ return self.discover_metamodel.use_operation(
454
+ self.sat_model, "PySATBackbone"
377
455
  ).get_result()
378
- return result
456
+ except FlamaException as exception:
457
+ logger.error("Error: %s", exception)
458
+ return None
459
+
460
+ # BDD-based operations
461
+
462
+ def homogeneity(self) -> Union[None, float]:
463
+ """
464
+ Measures how similar the products of the feature model are to each other. It is
465
+ computed as the average commonality factor across all features. A value of 1.0 means
466
+ all products are identical; lower values indicate more diversity.
467
+ """
468
+ try:
469
+ self._transform_to_bdd()
470
+ return self.discover_metamodel.use_operation(
471
+ self.bdd_model, "BDDHomogeneity"
472
+ ).get_result()
473
+ except FlamaException as exception:
474
+ logger.error("Error: %s", exception)
475
+ return None
476
+
477
+ def product_distribution(self) -> Union[None, List[int]]:
478
+ """
479
+ Returns the distribution of products by number of activated features. The value at
480
+ index i is the count of valid configurations that have exactly i features selected.
481
+ """
482
+ try:
483
+ self._transform_to_bdd()
484
+ return self.discover_metamodel.use_operation(
485
+ self.bdd_model, "BDDProductDistribution"
486
+ ).get_result()
487
+ except FlamaException as exception:
488
+ logger.error("Error: %s", exception)
489
+ return None
490
+
491
+ def variability(self) -> Union[None, Tuple[float, float]]:
492
+ """
493
+ Returns a tuple of (total_variability, partial_variability). Total variability is the
494
+ ratio of valid configurations to the theoretical maximum; partial variability is the
495
+ ratio relative to only the variant features.
496
+ """
497
+ try:
498
+ self._transform_to_bdd()
499
+ return self.discover_metamodel.use_operation(
500
+ self.bdd_model, "BDDVariability"
501
+ ).get_result()
502
+ except FlamaException as exception:
503
+ logger.error("Error: %s", exception)
504
+ return None
505
+
506
+ def variant_features(self) -> Union[None, List[str]]:
507
+ """
508
+ Returns the features that are neither core nor dead — they appear in some but not all
509
+ valid configurations. These are the features that actually vary across products.
510
+ """
511
+ try:
512
+ self._transform_to_bdd()
513
+ return self.discover_metamodel.use_operation(
514
+ self.bdd_model, "BDDVariantFeatures"
515
+ ).get_result()
516
+ except FlamaException as exception:
517
+ logger.error("Error: %s", exception)
518
+ return None
519
+
520
+ def unique_features(self) -> Union[None, List[str]]:
521
+ """
522
+ Returns features that appear in exactly one valid configuration. These features
523
+ uniquely identify a single product in the product line.
524
+ """
525
+ try:
526
+ self._transform_to_bdd()
527
+ return self.discover_metamodel.use_operation(
528
+ self.bdd_model, "BDDUniqueFeatures"
529
+ ).get_result()
530
+ except FlamaException as exception:
531
+ logger.error("Error: %s", exception)
532
+ return None
533
+
534
+ def pure_optional_features(self) -> Union[None, List[str]]:
535
+ """
536
+ Returns features with a feature inclusion probability of exactly 0.5, meaning they
537
+ are selected in exactly half of the valid configurations. These are the most
538
+ unconstrained optional features.
539
+ """
540
+ try:
541
+ self._transform_to_bdd()
542
+ return self.discover_metamodel.use_operation(
543
+ self.bdd_model, "BDDPureOptionalFeatures"
544
+ ).get_result()
545
+ except FlamaException as exception:
546
+ logger.error("Error: %s", exception)
547
+ return None
548
+
549
+ def feature_inclusion_probability(self) -> Union[None, Dict[str, float]]:
550
+ """
551
+ Returns a mapping from each feature name to its inclusion probability: the fraction
552
+ of valid configurations in which that feature is selected. Core features have
553
+ probability 1.0 and dead features have probability 0.0.
554
+ """
555
+ try:
556
+ self._transform_to_bdd()
557
+ return self.discover_metamodel.use_operation(
558
+ self.bdd_model, "BDDFeatureInclusionProbability"
559
+ ).get_result()
560
+ except FlamaException as exception:
561
+ logger.error("Error: %s", exception)
562
+ return None
563
+
564
+ def configurations_with_n_features(self, n: int) -> Union[None, List[Configuration]]:
565
+ """
566
+ Returns all valid configurations that have exactly n features selected.
567
+ """
568
+ try:
569
+ self._transform_to_bdd()
570
+ operation = self.discover_metamodel.get_operation(
571
+ self.bdd_model, "BDDConfigurationsWithNFeatures"
572
+ )
573
+ operation.set_n_features(n)
574
+ operation.execute(self.bdd_model)
575
+ return list(operation.get_result())
576
+ except FlamaException as exception:
577
+ logger.error("Error: %s", exception)
578
+ return None
579
+
580
+ def sampling(
581
+ self, size: int, with_replacement: bool = False, backend: Optional[str] = None
582
+ ) -> Union[None, List[Configuration]]:
583
+ """
584
+ Returns a random sample of valid configurations of the given size. When
585
+ with_replacement is True, the same configuration may appear more than once.
586
+
587
+ ``backend`` selects the analysis plugin; defaults to bdd. Only sat and bdd
588
+ implement sampling.
589
+ """
590
+ try:
591
+ model = self._select_backend(backend, Backend.BDD)
592
+ operation = self.discover_metamodel.get_operation(model, "Sampling")
593
+ operation.set_sample_size(size)
594
+ operation.set_with_replacement(with_replacement)
595
+ operation.execute(model)
596
+ return operation.get_result()
597
+ except FlamaException as exception:
598
+ logger.error("Error: %s", exception)
599
+ return None
600
+
601
+ # Z3-based operations
602
+
603
+ def all_feature_bounds(self) -> Union[None, Dict[str, Dict[str, Any]]]:
604
+ """
605
+ Returns the value bounds for all typed (Integer, Real, String) attribute features in
606
+ the model. Each entry maps a feature name to a dict with 'min', 'max', and 'bounded'
607
+ keys. Requires the z3 metamodel plugin.
608
+ """
609
+ try:
610
+ self._transform_to_z3()
611
+ return self.discover_metamodel.use_operation(
612
+ self.z3_model, "Z3AllFeatureBounds"
613
+ ).get_result()
614
+ except FlamaException as exception:
615
+ logger.error("Error: %s", exception)
616
+ return None
617
+
618
+ def feature_bounds(self, variable_name: str) -> Union[None, Dict[str, Any]]:
619
+ """
620
+ Returns the value bounds for a specific typed attribute feature. The result dict
621
+ contains 'min', 'max', and 'bounded' keys. Requires the z3 metamodel plugin.
622
+ """
623
+ try:
624
+ self._transform_to_z3()
625
+ operation = self.discover_metamodel.get_operation(self.z3_model, "Z3FeatureBounds")
626
+ operation.set_variable_name(variable_name)
627
+ operation.execute(self.z3_model)
628
+ return operation.get_result()
629
+ except FlamaException as exception:
630
+ logger.error("Error: %s", exception)
631
+ return None
632
+
633
+ # Diagnosis-based operations
634
+
635
+ def diagnosis(
636
+ self,
637
+ configuration_path: str,
638
+ test_case_path: str,
639
+ max_diagnoses: Optional[int] = None,
640
+ ) -> Union[None, List[str]]:
641
+ """
642
+ Returns a list of diagnoses explaining why a given configuration does not satisfy
643
+ the test case constraints. Each diagnosis is a minimal set of constraints whose
644
+ removal would resolve the inconsistency. Requires the pysat_diagnosis plugin.
645
+ """
646
+ try:
647
+ self._transform_to_diagnosis()
648
+ configuration = self.discover_metamodel.use_transformation_t2m(
649
+ configuration_path, "configuration"
650
+ )
651
+ test_case = self.discover_metamodel.use_transformation_t2m(
652
+ test_case_path, "configuration"
653
+ )
654
+ operation = self.discover_metamodel.get_operation(
655
+ self.diagnosis_model, "PySATDiagnosis"
656
+ )
657
+ operation.set_configuration(configuration)
658
+ operation.set_test_case(test_case)
659
+ if max_diagnoses is not None:
660
+ operation.set_max_diagnoses(max_diagnoses)
661
+ operation.execute(self.diagnosis_model)
662
+ return operation.get_result()
663
+ except FlamaException as exception:
664
+ logger.error("Error: %s", exception)
665
+ return None
666
+
667
+ def conflict(
668
+ self,
669
+ configuration_path: str,
670
+ test_case_path: str,
671
+ max_conflicts: Optional[int] = None,
672
+ ) -> Union[None, List[str]]:
673
+ """
674
+ Returns a list of conflict sets: minimal subsets of the model constraints that are
675
+ inconsistent with the given configuration and test case. Requires the pysat_diagnosis
676
+ plugin.
677
+ """
678
+ try:
679
+ self._transform_to_diagnosis()
680
+ configuration = self.discover_metamodel.use_transformation_t2m(
681
+ configuration_path, "configuration"
682
+ )
683
+ test_case = self.discover_metamodel.use_transformation_t2m(
684
+ test_case_path, "configuration"
685
+ )
686
+ operation = self.discover_metamodel.get_operation(
687
+ self.diagnosis_model, "PySATConflict"
688
+ )
689
+ operation.set_configuration(configuration)
690
+ operation.set_test_case(test_case)
691
+ if max_conflicts is not None:
692
+ operation.set_max_conflicts(max_conflicts)
693
+ operation.execute(self.diagnosis_model)
694
+ return operation.get_result()
379
695
  except FlamaException as exception:
380
696
  logger.error("Error: %s", exception)
381
697
  return None
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.4
2
+ Name: flamapy
3
+ Version: 2.6.0.dev1
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.dev1
12
+ Requires-Dist: flamapy-fm~=2.6.0.dev1
13
+ Requires-Dist: flamapy-sat~=2.6.0.dev1
14
+ Requires-Dist: flamapy-bdd~=2.6.0.dev1
15
+ Requires-Dist: flamapy-z3~=2.6.0.dev1
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,9 @@
1
+ flamapy/commands/__init__.py,sha256=S9KJB8u4Ww5_6KQYs70iwQFbtDm08J7UvE7vxAB6DeE,6609
2
+ flamapy/interfaces/python/__init__.py,sha256=NVQH1l6O9ElD8YvUVBXJN0Y_ETmu3AbyQBZJJ9qiuoU,86
3
+ flamapy/interfaces/python/flamapy_feature_model.py,sha256=2wM0rdu2BS9vPuVlGfWwmPA1RsTPQLiDLiyM0BMjE1M,29296
4
+ flamapy-2.6.0.dev1.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
5
+ flamapy-2.6.0.dev1.dist-info/METADATA,sha256=eGRbi8iaytKJf4lWvtkJJxuaExnCZoTu8t6d37hMHSY,1264
6
+ flamapy-2.6.0.dev1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
7
+ flamapy-2.6.0.dev1.dist-info/entry_points.txt,sha256=CS_W4K9LW9xOf-7ymWD58KklNHBnhMHm0RXAZ9JLgVo,57
8
+ flamapy-2.6.0.dev1.dist-info/top_level.txt,sha256=2YBI9dg4GVk28AJuyNu6dldi5Riwt5SP0zpNe2lSU5M,8
9
+ flamapy-2.6.0.dev1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.0)
2
+ Generator: setuptools (82.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,104 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: flamapy
3
- Version: 2.5.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.5.0.dev0
12
- Requires-Dist: flamapy-fm~=2.5.0.dev0
13
- Requires-Dist: flamapy-sat~=2.5.0.dev0
14
- Requires-Dist: flamapy-bdd~=2.5.0.dev0
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="">[![Pytest Testing Suite](https://github.com/flamapy/flamapy/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/flamapy/flamapy/actions/workflows/tests.yml)</a>
26
- <a href="">[![Commits Syntax Checker](https://github.com/flamapy/flamapy/actions/workflows/commits.yml/badge.svg?branch=main)](https://github.com/flamapy/flamapy/actions/workflows/commits.yml)</a>
27
- <a href="">![PyPI](https://img.shields.io/pypi/v/flamapy?label=pypi%20package)
28
- <a href="">![PyPI - Downloads](https://img.shields.io/pypi/dm/flamapy)
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
-
@@ -1,9 +0,0 @@
1
- flamapy/commands/__init__.py,sha256=np1430SLtfuyxNTAFPFNGrB0OunU-KMrOnwWYcwa0VY,6550
2
- flamapy/interfaces/python/__init__.py,sha256=NVQH1l6O9ElD8YvUVBXJN0Y_ETmu3AbyQBZJJ9qiuoU,86
3
- flamapy/interfaces/python/flamapy_feature_model.py,sha256=lNjKO-1VQCw0s7QT0uWcfwVvCM7h2eISSfMbSL7BLhE,15918
4
- flamapy-2.5.0.dev0.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
5
- flamapy-2.5.0.dev0.dist-info/METADATA,sha256=mu_DJDpFhEbEc3Lp0Y23t4nCfTUD4WLduq31-7o-7HI,7733
6
- flamapy-2.5.0.dev0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
7
- flamapy-2.5.0.dev0.dist-info/entry_points.txt,sha256=CS_W4K9LW9xOf-7ymWD58KklNHBnhMHm0RXAZ9JLgVo,57
8
- flamapy-2.5.0.dev0.dist-info/top_level.txt,sha256=2YBI9dg4GVk28AJuyNu6dldi5Riwt5SP0zpNe2lSU5M,8
9
- flamapy-2.5.0.dev0.dist-info/RECORD,,