flamapy-dnnf 2.6.0.dev12__tar.gz → 2.9.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_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/PKG-INFO +3 -3
- flamapy_dnnf-2.9.0.dev0/flamapy/metamodels/dnnf_metamodel/__init__.py +35 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy/metamodels/dnnf_metamodel/models/dnnf_model.py +3 -2
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy/metamodels/dnnf_metamodel/transformations/fm_to_dnnf.py +2 -2
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy_dnnf.egg-info/PKG-INFO +3 -3
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy_dnnf.egg-info/SOURCES.txt +1 -0
- flamapy_dnnf-2.9.0.dev0/flamapy_dnnf.egg-info/entry_points.txt +2 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy_dnnf.egg-info/requires.txt +2 -2
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/pyproject.toml +6 -3
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/tests/test_dnnf.py +63 -0
- flamapy_dnnf-2.6.0.dev12/flamapy/metamodels/dnnf_metamodel/__init__.py +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/LICENSE +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/README.md +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy/metamodels/dnnf_metamodel/models/__init__.py +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy/metamodels/dnnf_metamodel/operations/__init__.py +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy/metamodels/dnnf_metamodel/operations/d4_runner.py +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy/metamodels/dnnf_metamodel/operations/dnnf_configurations_number.py +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy/metamodels/dnnf_metamodel/operations/dnnf_satisfiable.py +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy/metamodels/dnnf_metamodel/transformations/__init__.py +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy_dnnf.egg-info/dependency_links.txt +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy_dnnf.egg-info/top_level.txt +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/setup.cfg +0 -0
- {flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flamapy-dnnf
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.0.dev0
|
|
4
4
|
Summary: flamapy-dnnf is a plugin to flamapy providing exact analysis of feature models via d-DNNF knowledge compilation with the d4 compiler
|
|
5
5
|
Author-email: Flamapy <flamapy@us.es>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -8,8 +8,8 @@ Project-URL: Homepage, https://github.com/flamapy/dnnf_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.
|
|
12
|
-
Requires-Dist: flamapy-fm~=2.
|
|
11
|
+
Requires-Dist: flamapy-fw~=2.9.0.dev0
|
|
12
|
+
Requires-Dist: flamapy-fm~=2.9.0.dev0
|
|
13
13
|
Provides-Extra: dev
|
|
14
14
|
Requires-Dist: pytest; extra == "dev"
|
|
15
15
|
Requires-Dist: pytest-mock; extra == "dev"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from flamapy.core.exceptions import FlamaException
|
|
2
|
+
from flamapy.core.manifest import PluginManifest, OperationCapability
|
|
3
|
+
from flamapy.core.models.language_level import LanguageLevel, MajorLevel, MinorLevel
|
|
4
|
+
from flamapy.metamodels.dnnf_metamodel.models.dnnf_model import locate_d4
|
|
5
|
+
|
|
6
|
+
# d-DNNF compilation scales further than BDDs for counting. The cost is a placeholder: the
|
|
7
|
+
# 2026-11-06 calibration machine had no d4, so this plugin is the one without measured curves.
|
|
8
|
+
# It declares no ``calibration``, so the planner only picks it when no calibrated plugin can
|
|
9
|
+
# run the operation.
|
|
10
|
+
# d4 is a native binary with no PyPI distribution (see models.locate_d4): without it the
|
|
11
|
+
# operations fail (the transformation, which only writes the DIMACS file, still runs), so the
|
|
12
|
+
# manifest declares the plugin unavailable and the planner never routes to it. The check runs
|
|
13
|
+
# once, when the plugin is imported: set FLAMAPY_D4_PATH before that.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _d4_available() -> bool:
|
|
17
|
+
try:
|
|
18
|
+
locate_d4()
|
|
19
|
+
except FlamaException:
|
|
20
|
+
return False
|
|
21
|
+
return True
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_D4_AVAILABLE = _d4_available()
|
|
25
|
+
|
|
26
|
+
MANIFEST = PluginManifest(
|
|
27
|
+
name='dnnf',
|
|
28
|
+
extension='dnnf',
|
|
29
|
+
available=_D4_AVAILABLE,
|
|
30
|
+
unavailable_reason='the d4 compiler is not installed (set FLAMAPY_D4_PATH or put d4 on PATH)',
|
|
31
|
+
supported_level=LanguageLevel(MajorLevel.BOOLEAN, {MinorLevel.GROUP_CARDINALITY}),
|
|
32
|
+
operations={
|
|
33
|
+
'configurations_number': OperationCapability(cost=80),
|
|
34
|
+
},
|
|
35
|
+
)
|
|
@@ -14,9 +14,10 @@ def locate_d4() -> str:
|
|
|
14
14
|
installed separately.
|
|
15
15
|
"""
|
|
16
16
|
candidate = os.environ.get('FLAMAPY_D4_PATH') or shutil.which('d4')
|
|
17
|
-
if not candidate or not os.path.
|
|
17
|
+
if not candidate or not os.path.isfile(candidate) or not os.access(candidate, os.X_OK):
|
|
18
18
|
raise FlamaException(
|
|
19
|
-
'The d4 compiler was not found. Install d4
|
|
19
|
+
'The d4 compiler was not found (or is not executable). Install d4 '
|
|
20
|
+
'(https://github.com/crillab/d4) and '
|
|
20
21
|
'expose it as "d4" on PATH or via the FLAMAPY_D4_PATH environment variable.'
|
|
21
22
|
)
|
|
22
23
|
return candidate
|
|
@@ -3,7 +3,7 @@ import subprocess
|
|
|
3
3
|
import tempfile
|
|
4
4
|
|
|
5
5
|
from flamapy.core.transformations import ModelToModel
|
|
6
|
-
from flamapy.metamodels.fm_metamodel.models import
|
|
6
|
+
from flamapy.metamodels.fm_metamodel.models import ClauseSet, DEFAULT_CNF_METHOD, FeatureModel
|
|
7
7
|
from flamapy.metamodels.dnnf_metamodel.models import DDNNFModel, locate_d4
|
|
8
8
|
|
|
9
9
|
|
|
@@ -26,7 +26,7 @@ class FmToDDNNF(ModelToModel):
|
|
|
26
26
|
def __init__(
|
|
27
27
|
self,
|
|
28
28
|
source_model: FeatureModel,
|
|
29
|
-
cnf_method: str =
|
|
29
|
+
cnf_method: str = DEFAULT_CNF_METHOD,
|
|
30
30
|
compile: bool = False,
|
|
31
31
|
) -> None:
|
|
32
32
|
self.source_model = source_model
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flamapy-dnnf
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.0.dev0
|
|
4
4
|
Summary: flamapy-dnnf is a plugin to flamapy providing exact analysis of feature models via d-DNNF knowledge compilation with the d4 compiler
|
|
5
5
|
Author-email: Flamapy <flamapy@us.es>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -8,8 +8,8 @@ Project-URL: Homepage, https://github.com/flamapy/dnnf_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.
|
|
12
|
-
Requires-Dist: flamapy-fm~=2.
|
|
11
|
+
Requires-Dist: flamapy-fw~=2.9.0.dev0
|
|
12
|
+
Requires-Dist: flamapy-fm~=2.9.0.dev0
|
|
13
13
|
Provides-Extra: dev
|
|
14
14
|
Requires-Dist: pytest; extra == "dev"
|
|
15
15
|
Requires-Dist: pytest-mock; extra == "dev"
|
|
@@ -14,6 +14,7 @@ flamapy/metamodels/dnnf_metamodel/transformations/fm_to_dnnf.py
|
|
|
14
14
|
flamapy_dnnf.egg-info/PKG-INFO
|
|
15
15
|
flamapy_dnnf.egg-info/SOURCES.txt
|
|
16
16
|
flamapy_dnnf.egg-info/dependency_links.txt
|
|
17
|
+
flamapy_dnnf.egg-info/entry_points.txt
|
|
17
18
|
flamapy_dnnf.egg-info/requires.txt
|
|
18
19
|
flamapy_dnnf.egg-info/top_level.txt
|
|
19
20
|
tests/test_dnnf.py
|
|
@@ -4,15 +4,15 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "flamapy-dnnf"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.9.0.dev0"
|
|
8
8
|
description = "flamapy-dnnf is a plugin to flamapy providing exact analysis of feature models via d-DNNF knowledge compilation with the d4 compiler"
|
|
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.
|
|
15
|
-
"flamapy-fm~=2.
|
|
14
|
+
"flamapy-fw~=2.9.0.dev0",
|
|
15
|
+
"flamapy-fm~=2.9.0.dev0",
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
[project.optional-dependencies]
|
|
@@ -24,6 +24,9 @@ dev = [
|
|
|
24
24
|
"coverage",
|
|
25
25
|
]
|
|
26
26
|
|
|
27
|
+
[project.entry-points."flamapy.plugins"]
|
|
28
|
+
dnnf = "flamapy.metamodels.dnnf_metamodel"
|
|
29
|
+
|
|
27
30
|
[project.urls]
|
|
28
31
|
Homepage = "https://github.com/flamapy/dnnf_metamodel"
|
|
29
32
|
|
|
@@ -82,3 +82,66 @@ def test_transformation_writes_dimacs():
|
|
|
82
82
|
header = dimacs.readline()
|
|
83
83
|
assert header.startswith('p cnf ')
|
|
84
84
|
assert model.var_count >= 5 # Root, Base, A, B, C
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
_TSEYTIN_UVL = """features
|
|
88
|
+
Root {abstract}
|
|
89
|
+
optional
|
|
90
|
+
A
|
|
91
|
+
B
|
|
92
|
+
C
|
|
93
|
+
D
|
|
94
|
+
E
|
|
95
|
+
F
|
|
96
|
+
constraints
|
|
97
|
+
(A & B & C) | (D & E & F) | (B & E & F)
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
# [1..2] over A, B, C (3 + 3 selections) times D optional: 12 configurations.
|
|
101
|
+
_GROUP_UVL = """features
|
|
102
|
+
Root {abstract}
|
|
103
|
+
[1..2]
|
|
104
|
+
A
|
|
105
|
+
B
|
|
106
|
+
C
|
|
107
|
+
optional
|
|
108
|
+
D
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_tseytin_adds_variables_to_the_dimacs():
|
|
113
|
+
"""Without d4: the 'auto'/'tseytin' CNF written for d4 holds the auxiliary variables."""
|
|
114
|
+
fm = _fm(_TSEYTIN_UVL)
|
|
115
|
+
plain = FmToDDNNF(fm, cnf_method='distributive').transform()
|
|
116
|
+
gated = FmToDDNNF(fm, cnf_method='tseytin').transform()
|
|
117
|
+
assert gated.var_count > plain.var_count == 7 # Root and A-F
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@requires_d4
|
|
121
|
+
def test_cnf_methods_agree():
|
|
122
|
+
fm = _fm(_TSEYTIN_UVL)
|
|
123
|
+
exact = _exact_count(fm)
|
|
124
|
+
for method in ('distributive', 'tseytin', 'auto'):
|
|
125
|
+
model = FmToDDNNF(fm, cnf_method=method).transform()
|
|
126
|
+
assert DDNNFConfigurationsNumber().execute(model).get_result() == exact, method
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@requires_d4
|
|
130
|
+
def test_group_cardinality():
|
|
131
|
+
model = FmToDDNNF(_fm(_GROUP_UVL)).transform()
|
|
132
|
+
assert DDNNFConfigurationsNumber().execute(model).get_result() == 12
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_availability_follows_locate_d4(monkeypatch, tmp_path):
|
|
136
|
+
import importlib # noqa: PLC0415
|
|
137
|
+
import flamapy.metamodels.dnnf_metamodel as plugin # noqa: PLC0415
|
|
138
|
+
monkeypatch.setenv('FLAMAPY_D4_PATH', str(tmp_path / 'missing-d4'))
|
|
139
|
+
monkeypatch.setattr(shutil, 'which', lambda _name: None)
|
|
140
|
+
assert importlib.reload(plugin).MANIFEST.available is False
|
|
141
|
+
fake = tmp_path / 'd4'
|
|
142
|
+
fake.write_text('#!/bin/sh\n')
|
|
143
|
+
fake.chmod(0o755)
|
|
144
|
+
monkeypatch.setenv('FLAMAPY_D4_PATH', str(fake))
|
|
145
|
+
assert importlib.reload(plugin).MANIFEST.available is True
|
|
146
|
+
monkeypatch.undo()
|
|
147
|
+
importlib.reload(plugin)
|
|
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
|
{flamapy_dnnf-2.6.0.dev12 → flamapy_dnnf-2.9.0.dev0}/flamapy_dnnf.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|