flamapy-sharpsat 2.6.0.dev5__tar.gz → 2.6.0.dev12__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.
Files changed (22) hide show
  1. {flamapy_sharpsat-2.6.0.dev5/flamapy_sharpsat.egg-info → flamapy_sharpsat-2.6.0.dev12}/PKG-INFO +3 -4
  2. flamapy_sharpsat-2.6.0.dev12/flamapy/metamodels/sharpsat_metamodel/transformations/fm_to_sharpsat.py +35 -0
  3. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12/flamapy_sharpsat.egg-info}/PKG-INFO +3 -4
  4. flamapy_sharpsat-2.6.0.dev12/flamapy_sharpsat.egg-info/requires.txt +10 -0
  5. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/pyproject.toml +3 -4
  6. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/tests/test_sharpsat.py +27 -19
  7. flamapy_sharpsat-2.6.0.dev5/flamapy/metamodels/sharpsat_metamodel/transformations/fm_to_sharpsat.py +0 -41
  8. flamapy_sharpsat-2.6.0.dev5/flamapy_sharpsat.egg-info/requires.txt +0 -11
  9. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/LICENSE +0 -0
  10. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/README.md +0 -0
  11. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/flamapy/metamodels/sharpsat_metamodel/__init__.py +0 -0
  12. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/flamapy/metamodels/sharpsat_metamodel/models/__init__.py +0 -0
  13. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/flamapy/metamodels/sharpsat_metamodel/models/sharpsat_model.py +0 -0
  14. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/flamapy/metamodels/sharpsat_metamodel/operations/__init__.py +0 -0
  15. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/flamapy/metamodels/sharpsat_metamodel/operations/sharpsat_configurations_number.py +0 -0
  16. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/flamapy/metamodels/sharpsat_metamodel/operations/sharpsat_sampling.py +0 -0
  17. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/flamapy/metamodels/sharpsat_metamodel/transformations/__init__.py +0 -0
  18. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/flamapy_sharpsat.egg-info/SOURCES.txt +0 -0
  19. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/flamapy_sharpsat.egg-info/dependency_links.txt +0 -0
  20. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/flamapy_sharpsat.egg-info/top_level.txt +0 -0
  21. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/setup.cfg +0 -0
  22. {flamapy_sharpsat-2.6.0.dev5 → flamapy_sharpsat-2.6.0.dev12}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flamapy-sharpsat
3
- Version: 2.6.0.dev5
3
+ Version: 2.6.0.dev12
4
4
  Summary: flamapy-sharpsat is a plugin to flamapy providing approximate model counting and almost-uniform sampling
5
5
  Author-email: Flamapy <flamapy@us.es>
6
6
  License-Expression: GPL-3.0-or-later
@@ -8,9 +8,8 @@ Project-URL: Homepage, https://github.com/flamapy/sharpsat_metamodel
8
8
  Requires-Python: >=3.11
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: flamapy-fw~=2.6.0.dev5
12
- Requires-Dist: flamapy-fm~=2.6.0.dev5
13
- Requires-Dist: flamapy-sat~=2.6.0.dev5
11
+ Requires-Dist: flamapy-fw~=2.6.0.dev12
12
+ Requires-Dist: flamapy-fm~=2.6.0.dev12
14
13
  Requires-Dist: pyunigen~=2.5
15
14
  Provides-Extra: dev
16
15
  Requires-Dist: pytest; extra == "dev"
@@ -0,0 +1,35 @@
1
+ from flamapy.core.transformations import ModelToModel
2
+ from flamapy.metamodels.fm_metamodel.models import FeatureModel, ClauseSet
3
+ from flamapy.metamodels.sharpsat_metamodel.models import SharpSATModel
4
+
5
+
6
+ class FmToSharpSAT(ModelToModel):
7
+ """Transform a feature model into a SharpSAT (CNF) model.
8
+
9
+ The CNF is produced by the feature-model ``ClauseSet`` (no SAT-solver dependency), so the
10
+ same constraint encoding (and the optional Tseytin encoding via ``cnf_method``) is available
11
+ here.
12
+ """
13
+
14
+ @staticmethod
15
+ def get_source_extension() -> str:
16
+ return 'fm'
17
+
18
+ @staticmethod
19
+ def get_destination_extension() -> str:
20
+ return 'sharpsat'
21
+
22
+ def __init__(self, source_model: FeatureModel, cnf_method: str = 'distributive') -> None:
23
+ self.source_model = source_model
24
+ self.cnf_method = cnf_method
25
+ self.destination_model = SharpSATModel()
26
+
27
+ def transform(self) -> SharpSATModel:
28
+ clause_set = ClauseSet.from_feature_model(self.source_model, cnf_method=self.cnf_method)
29
+ model = self.destination_model
30
+ model.clauses = [list(clause) for clause in clause_set.clauses]
31
+ model.variables = dict(clause_set.variables)
32
+ model.features = dict(clause_set.features)
33
+ model.auxiliary_variables = set(clause_set.auxiliary_variables)
34
+ model.original_model = self.source_model
35
+ return model
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flamapy-sharpsat
3
- Version: 2.6.0.dev5
3
+ Version: 2.6.0.dev12
4
4
  Summary: flamapy-sharpsat is a plugin to flamapy providing approximate model counting and almost-uniform sampling
5
5
  Author-email: Flamapy <flamapy@us.es>
6
6
  License-Expression: GPL-3.0-or-later
@@ -8,9 +8,8 @@ Project-URL: Homepage, https://github.com/flamapy/sharpsat_metamodel
8
8
  Requires-Python: >=3.11
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: flamapy-fw~=2.6.0.dev5
12
- Requires-Dist: flamapy-fm~=2.6.0.dev5
13
- Requires-Dist: flamapy-sat~=2.6.0.dev5
11
+ Requires-Dist: flamapy-fw~=2.6.0.dev12
12
+ Requires-Dist: flamapy-fm~=2.6.0.dev12
14
13
  Requires-Dist: pyunigen~=2.5
15
14
  Provides-Extra: dev
16
15
  Requires-Dist: pytest; extra == "dev"
@@ -0,0 +1,10 @@
1
+ flamapy-fw~=2.6.0.dev12
2
+ flamapy-fm~=2.6.0.dev12
3
+ pyunigen~=2.5
4
+
5
+ [dev]
6
+ pytest
7
+ pytest-mock
8
+ prospector
9
+ mypy
10
+ coverage
@@ -4,16 +4,15 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "flamapy-sharpsat"
7
- version = "2.6.0.dev5"
7
+ version = "2.6.0.dev12"
8
8
  description = "flamapy-sharpsat is a plugin to flamapy providing approximate model counting and almost-uniform sampling"
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.11"
13
13
  dependencies = [
14
- "flamapy-fw~=2.6.0.dev5",
15
- "flamapy-fm~=2.6.0.dev5",
16
- "flamapy-sat~=2.6.0.dev5",
14
+ "flamapy-fw~=2.6.0.dev12",
15
+ "flamapy-fm~=2.6.0.dev12",
17
16
  "pyunigen~=2.5",
18
17
  ]
19
18
 
@@ -1,28 +1,42 @@
1
1
  """Tests for the sharpsat metamodel: approximate counting and almost-uniform sampling."""
2
2
  import os
3
3
  import tempfile
4
+ from itertools import product
4
5
 
5
6
  import pytest
6
7
 
7
8
  from flamapy.metamodels.fm_metamodel.transformations import UVLReader
8
- from flamapy.metamodels.fm_metamodel.models import FeatureModel
9
+ from flamapy.metamodels.fm_metamodel.models import FeatureModel, ClauseSet
9
10
  from flamapy.metamodels.sharpsat_metamodel.transformations import FmToSharpSAT
10
11
  from flamapy.metamodels.sharpsat_metamodel.operations import (
11
12
  SharpSATConfigurationsNumber,
12
13
  SharpSATSampling,
13
14
  )
14
-
15
- # Exact oracle and validity oracle via SAT (a declared dependency, unlike BDD).
16
- from flamapy.metamodels.pysat_metamodel.transformations import FmToPysat
17
- from flamapy.metamodels.pysat_metamodel.operations.pysat_configurations_number import (
18
- PySATConfigurationsNumber,
19
- )
20
- from flamapy.metamodels.pysat_metamodel.operations.pysat_satisfiable_configuration import (
21
- PySATSatisfiableConfiguration,
22
- )
23
15
  from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration
24
16
 
25
17
 
18
+ # Exact oracle: enumerate the valid configurations by brute-forcing the CNF produced by
19
+ # fm_metamodel's solver-agnostic ClauseSet — no SAT backend, so the tests only depend on
20
+ # flamapy-fm (already required) and match sharpsat's own encoding path.
21
+ def _valid_configs(fm: FeatureModel) -> set:
22
+ """Valid configurations as frozensets of selected feature names."""
23
+ clause_set = ClauseSet.from_feature_model(fm)
24
+ name_of = {vid: name for name, vid in clause_set.variables.items()}
25
+ var_ids = sorted({abs(lit) for clause in clause_set.clauses for lit in clause}
26
+ | set(clause_set.variables.values()))
27
+ configs = set()
28
+ for bits in product((False, True), repeat=len(var_ids)):
29
+ assignment = dict(zip(var_ids, bits))
30
+ if all(any((lit > 0) == assignment[abs(lit)] for lit in clause)
31
+ for clause in clause_set.clauses):
32
+ configs.add(frozenset(name_of[v] for v in var_ids if assignment[v] and v in name_of))
33
+ return configs
34
+
35
+
36
+ def _exact_count(fm: FeatureModel) -> int:
37
+ return len(_valid_configs(fm))
38
+
39
+
26
40
  _UVL = """features
27
41
  Root {abstract}
28
42
  optional
@@ -51,13 +65,7 @@ def _all_feature_names(fm: FeatureModel) -> list[str]:
51
65
 
52
66
 
53
67
  def _is_valid(fm: FeatureModel, selected: set) -> bool:
54
- sat_model = FmToPysat(fm).transform()
55
- elements = {name: (name in selected) for name in _all_feature_names(fm)}
56
- configuration = Configuration(elements)
57
- configuration.set_full(True)
58
- op = PySATSatisfiableConfiguration()
59
- op.set_configuration(configuration)
60
- return op.execute(sat_model).get_result()
68
+ return frozenset(selected) in _valid_configs(fm)
61
69
 
62
70
 
63
71
  _IMPLIED_VARS_UVL = """features
@@ -87,7 +95,7 @@ def _fm_from(uvl):
87
95
 
88
96
  def test_approximate_count_matches_exact_on_small_model():
89
97
  fm = _fm()
90
- exact = PySATConfigurationsNumber().execute(FmToPysat(fm).transform()).get_result()
98
+ exact = _exact_count(fm)
91
99
  approx = SharpSATConfigurationsNumber().execute(FmToSharpSAT(fm).transform()).get_result()
92
100
  # ApproxMC's default (epsilon, delta) is exact on a model this small.
93
101
  assert approx == exact
@@ -98,7 +106,7 @@ def test_count_is_correct_with_implied_variables():
98
106
  # must not be undercounted. The standalone pyapproxmc binding halved these; the UniGen
99
107
  # ApproxMC invocation used by the counter does not.
100
108
  fm = _fm_from(_IMPLIED_VARS_UVL)
101
- exact = PySATConfigurationsNumber().execute(FmToPysat(fm).transform()).get_result()
109
+ exact = _exact_count(fm)
102
110
  approx = SharpSATConfigurationsNumber().execute(FmToSharpSAT(fm).transform()).get_result()
103
111
  assert approx == exact
104
112
 
@@ -1,41 +0,0 @@
1
- from flamapy.core.transformations import ModelToModel
2
- from flamapy.metamodels.fm_metamodel.models import FeatureModel
3
- from flamapy.metamodels.pysat_metamodel.transformations import FmToPysat
4
- from flamapy.metamodels.sharpsat_metamodel.models import SharpSATModel
5
-
6
-
7
- class FmToSharpSAT(ModelToModel):
8
- """Transform a feature model into a SharpSAT (CNF) model.
9
-
10
- The CNF is produced by reusing the SAT metamodel's ``FmToPysat`` transformation, so
11
- the same constraint encoding (and the optional Tseytin encoding via ``cnf_method``) is
12
- available here.
13
- """
14
-
15
- @staticmethod
16
- def get_source_extension() -> str:
17
- return 'fm'
18
-
19
- @staticmethod
20
- def get_destination_extension() -> str:
21
- return 'sharpsat'
22
-
23
- def __init__(self, source_model: FeatureModel, cnf_method: str = 'distributive') -> None:
24
- self.source_model = source_model
25
- self.cnf_method = cnf_method
26
- self.destination_model = SharpSATModel()
27
-
28
- def transform(self) -> SharpSATModel:
29
- # Only pass cnf_method when a non-default encoding is requested, so the plugin also
30
- # works against releases of flamapy-sat that predate the Tseytin cnf_method option.
31
- if self.cnf_method == 'distributive':
32
- sat_model = FmToPysat(self.source_model).transform()
33
- else:
34
- sat_model = FmToPysat(self.source_model, cnf_method=self.cnf_method).transform()
35
- model = self.destination_model
36
- model.clauses = [list(clause) for clause in sat_model.get_all_clauses().clauses]
37
- model.variables = dict(sat_model.variables)
38
- model.features = dict(sat_model.features)
39
- model.auxiliary_variables = set(sat_model.auxiliary_variables)
40
- model.original_model = self.source_model
41
- return model
@@ -1,11 +0,0 @@
1
- flamapy-fw~=2.6.0.dev5
2
- flamapy-fm~=2.6.0.dev5
3
- flamapy-sat~=2.6.0.dev5
4
- pyunigen~=2.5
5
-
6
- [dev]
7
- pytest
8
- pytest-mock
9
- prospector
10
- mypy
11
- coverage