PySDD 1.0.7__cp315-cp315-win_amd64.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.
- pysdd/__init__.py +1 -0
- pysdd/__main__.py +19 -0
- pysdd/cli.py +261 -0
- pysdd/compiler_c.pxd +37 -0
- pysdd/fnf_c.pxd +17 -0
- pysdd/io_c.pxd +22 -0
- pysdd/iterator.py +223 -0
- pysdd/lib/libsdd-2.0/include/iterators.h +243 -0
- pysdd/lib/libsdd-2.0/include/macros.h +273 -0
- pysdd/lib/libsdd-2.0/include/parameters.h +71 -0
- pysdd/lib/libsdd-2.0/include/sdd.h +823 -0
- pysdd/lib/libsdd-2.0/include/stacks.h +86 -0
- pysdd/lib/libsdd-2.0/src/basic/computed.c +91 -0
- pysdd/lib/libsdd-2.0/src/basic/count_and_size.c +173 -0
- pysdd/lib/libsdd-2.0/src/basic/gc.c +104 -0
- pysdd/lib/libsdd-2.0/src/basic/hash.c +221 -0
- pysdd/lib/libsdd-2.0/src/basic/memory.c +180 -0
- pysdd/lib/libsdd-2.0/src/basic/multiply.c +209 -0
- pysdd/lib/libsdd-2.0/src/basic/nodes.c +174 -0
- pysdd/lib/libsdd-2.0/src/basic/partitions.c +351 -0
- pysdd/lib/libsdd-2.0/src/basic/references.c +79 -0
- pysdd/lib/libsdd-2.0/src/basic/replace.c +91 -0
- pysdd/lib/libsdd-2.0/src/basic/shadows.c +384 -0
- pysdd/lib/libsdd-2.0/src/basic/sort.c +148 -0
- pysdd/lib/libsdd-2.0/src/manager/copy.c +84 -0
- pysdd/lib/libsdd-2.0/src/manager/interface.c +411 -0
- pysdd/lib/libsdd-2.0/src/manager/manager.c +308 -0
- pysdd/lib/libsdd-2.0/src/manager/stats.c +121 -0
- pysdd/lib/libsdd-2.0/src/manager/variables.c +267 -0
- pysdd/lib/libsdd-2.0/src/sdds/apply.c +396 -0
- pysdd/lib/libsdd-2.0/src/sdds/bits.c +156 -0
- pysdd/lib/libsdd-2.0/src/sdds/cardinality.c +313 -0
- pysdd/lib/libsdd-2.0/src/sdds/condition.c +107 -0
- pysdd/lib/libsdd-2.0/src/sdds/copy.c +117 -0
- pysdd/lib/libsdd-2.0/src/sdds/count.c +78 -0
- pysdd/lib/libsdd-2.0/src/sdds/essential_vars.c +128 -0
- pysdd/lib/libsdd-2.0/src/sdds/exists.c +29 -0
- pysdd/lib/libsdd-2.0/src/sdds/exists_multiple.c +311 -0
- pysdd/lib/libsdd-2.0/src/sdds/exists_multiple_static.c +165 -0
- pysdd/lib/libsdd-2.0/src/sdds/forall.c +28 -0
- pysdd/lib/libsdd-2.0/src/sdds/io.c +448 -0
- pysdd/lib/libsdd-2.0/src/sdds/model_count.c +146 -0
- pysdd/lib/libsdd-2.0/src/sdds/rename_vars.c +113 -0
- pysdd/lib/libsdd-2.0/src/sdds/size.c +65 -0
- pysdd/lib/libsdd-2.0/src/sdds/wmc.c +412 -0
- pysdd/lib/libsdd-2.0/src/util.c +255 -0
- pysdd/lib/libsdd-2.0/src/verify.c +236 -0
- pysdd/lib/libsdd-2.0/src/version.c +14 -0
- pysdd/lib/libsdd-2.0/src/vtree_fragments/construction.c +329 -0
- pysdd/lib/libsdd-2.0/src/vtree_fragments/moves.c +100 -0
- pysdd/lib/libsdd-2.0/src/vtree_fragments/operations.c +302 -0
- pysdd/lib/libsdd-2.0/src/vtree_operations/cartesian_product.c +166 -0
- pysdd/lib/libsdd-2.0/src/vtree_operations/dissect.c +78 -0
- pysdd/lib/libsdd-2.0/src/vtree_operations/limits.c +237 -0
- pysdd/lib/libsdd-2.0/src/vtree_operations/op_left_rotate.c +143 -0
- pysdd/lib/libsdd-2.0/src/vtree_operations/op_right_rotate.c +150 -0
- pysdd/lib/libsdd-2.0/src/vtree_operations/op_swap.c +112 -0
- pysdd/lib/libsdd-2.0/src/vtree_operations/rollback.c +68 -0
- pysdd/lib/libsdd-2.0/src/vtree_operations/split.c +180 -0
- pysdd/lib/libsdd-2.0/src/vtree_operations/vtree_ops.h +48 -0
- pysdd/lib/libsdd-2.0/src/vtree_search/auto.c +186 -0
- pysdd/lib/libsdd-2.0/src/vtree_search/search.c +332 -0
- pysdd/lib/libsdd-2.0/src/vtree_search/state.c +127 -0
- pysdd/lib/libsdd-2.0/src/vtrees/compare.c +138 -0
- pysdd/lib/libsdd-2.0/src/vtrees/edit.c +199 -0
- pysdd/lib/libsdd-2.0/src/vtrees/io.c +207 -0
- pysdd/lib/libsdd-2.0/src/vtrees/maps.c +42 -0
- pysdd/lib/libsdd-2.0/src/vtrees/moves.c +156 -0
- pysdd/lib/libsdd-2.0/src/vtrees/static.c +270 -0
- pysdd/lib/libsdd-2.0/src/vtrees/vtree.c +197 -0
- pysdd/lib/sdd-2.0/include/compiler.h +104 -0
- pysdd/lib/sdd-2.0/include/fnf.h +13 -0
- pysdd/lib/sdd-2.0/include/io.h +11 -0
- pysdd/lib/sdd-2.0/include/sddapi.h +341 -0
- pysdd/lib/sdd-2.0/src/fnf/compiler.c +120 -0
- pysdd/lib/sdd-2.0/src/fnf/fnf.c +57 -0
- pysdd/lib/sdd-2.0/src/fnf/io.c +201 -0
- pysdd/lib/sdd-2.0/src/fnf/utils.c +72 -0
- pysdd/lib/sdd_extra/include/sddapi_extra.h +22 -0
- pysdd/sdd.cp315-win_amd64.pyd +0 -0
- pysdd/sdd.pyx +1808 -0
- pysdd/sddapi_c.pxd +241 -0
- pysdd/util.py +450 -0
- pysdd/wmcstochastic.py +188 -0
- pysdd-1.0.7.dist-info/METADATA +216 -0
- pysdd-1.0.7.dist-info/RECORD +90 -0
- pysdd-1.0.7.dist-info/WHEEL +5 -0
- pysdd-1.0.7.dist-info/entry_points.txt +2 -0
- pysdd-1.0.7.dist-info/licenses/LICENSE +13 -0
- pysdd-1.0.7.dist-info/top_level.txt +1 -0
pysdd/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
pysdd/__main__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
pysdd.__main__
|
|
4
|
+
~~~~~~~~~~~~~~
|
|
5
|
+
|
|
6
|
+
Command Line Interface.
|
|
7
|
+
|
|
8
|
+
Will be run when `python -m pysdd` is used.
|
|
9
|
+
|
|
10
|
+
:author: Wannes Meert, Arthur Choi
|
|
11
|
+
:copyright: Copyright 2017-2019 KU Leuven and Regents of the University of California.
|
|
12
|
+
:license: Apache License, Version 2.0, see LICENSE for details.
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
from .cli import main
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
main()
|
pysdd/cli.py
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# encoding: utf-8
|
|
3
|
+
"""
|
|
4
|
+
pysdd-cli
|
|
5
|
+
~~~~~~~~~
|
|
6
|
+
|
|
7
|
+
PySDD command line interface.
|
|
8
|
+
|
|
9
|
+
:author: Wannes Meert, Arthur Choi
|
|
10
|
+
:copyright: Copyright 2019 KU Leuven and Regents of the University of California.
|
|
11
|
+
:license: Apache License, Version 2.0, see LICENSE for details.
|
|
12
|
+
"""
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
import argparse
|
|
17
|
+
import logging
|
|
18
|
+
from .sdd import SddManager, Vtree, Fnf, CompilerOptions
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def main(argv=None):
|
|
25
|
+
options, args = getopt(argv)
|
|
26
|
+
fnf = None
|
|
27
|
+
weights = None
|
|
28
|
+
|
|
29
|
+
if options.cnf_filename is not None:
|
|
30
|
+
print("reading cnf...")
|
|
31
|
+
fnf = Fnf.from_cnf_file(bytes(options.cnf_filename))
|
|
32
|
+
weights = read_weights(options.cnf_filename)
|
|
33
|
+
elif options.dnf_filename is not None:
|
|
34
|
+
print("reading dnf...")
|
|
35
|
+
fnf = Fnf.from_dnf_file(bytes(options.dnf_filename))
|
|
36
|
+
weights = read_weights(options.dnf_filename)
|
|
37
|
+
|
|
38
|
+
if options.vtree_filename is not None:
|
|
39
|
+
print("reading initial vtree...")
|
|
40
|
+
vtree = Vtree.from_file(bytes(options.vtree_filename))
|
|
41
|
+
else:
|
|
42
|
+
if fnf is None:
|
|
43
|
+
raise argparse.ArgumentTypeError("CNF or DNF file required")
|
|
44
|
+
print(f"creating initial vtree {options.initial_vtree_type.decode()}")
|
|
45
|
+
vtree = Vtree(var_count=fnf.var_count, vtree_type=options.initial_vtree_type)
|
|
46
|
+
|
|
47
|
+
print("creating manager...")
|
|
48
|
+
manager = SddManager.from_vtree(vtree)
|
|
49
|
+
manager.set_options(options)
|
|
50
|
+
|
|
51
|
+
if options.sdd_filename is None:
|
|
52
|
+
print("compiling...")
|
|
53
|
+
c1 = time.time()
|
|
54
|
+
node = manager.fnf_to_sdd(fnf)
|
|
55
|
+
c2 = time.time()
|
|
56
|
+
secs = c2 - c1
|
|
57
|
+
print("")
|
|
58
|
+
print(f"compilation time : {secs:.3f} sec")
|
|
59
|
+
else:
|
|
60
|
+
print("reading sdd from file...")
|
|
61
|
+
c1 = time.time()
|
|
62
|
+
node = manager.read_sdd_file(options.sdd_filename)
|
|
63
|
+
c2 = time.time()
|
|
64
|
+
secs = c2 - c1
|
|
65
|
+
print(f"read time : {secs:.3f} sec")
|
|
66
|
+
weights = read_weights(options.sdd_filename)
|
|
67
|
+
|
|
68
|
+
wmc = create_wmc(node, weights, args)
|
|
69
|
+
|
|
70
|
+
print_node(node, wmc)
|
|
71
|
+
if options.verbose:
|
|
72
|
+
manager.print_stdout()
|
|
73
|
+
|
|
74
|
+
if options.minimize_cardinality:
|
|
75
|
+
print("\nminimizing cardinality...", end="")
|
|
76
|
+
c1 = time.time()
|
|
77
|
+
node = manager.global_minimize_cardinality(node)
|
|
78
|
+
c2 = time.time()
|
|
79
|
+
min_card = manager.minimum_cardinality(node)
|
|
80
|
+
print("")
|
|
81
|
+
wmc = create_wmc(node, weights, args)
|
|
82
|
+
print_node(node, wmc)
|
|
83
|
+
print(f" min cardinality : {min_card} {c2-c1:.3f} sec")
|
|
84
|
+
|
|
85
|
+
manager_vtree = manager.vtree()
|
|
86
|
+
|
|
87
|
+
if options.post_search:
|
|
88
|
+
node.ref()
|
|
89
|
+
print("dynamic vtree (post compilation)")
|
|
90
|
+
print(f" sdd initial size : {node.size()}")
|
|
91
|
+
c1 = time.time()
|
|
92
|
+
manager.minimize_limited()
|
|
93
|
+
c2 = time.time()
|
|
94
|
+
print(f" dynamic vtree time : {c2-c1:.3f} sec")
|
|
95
|
+
wmc = create_wmc(node, weights, args)
|
|
96
|
+
print_node(node, wmc)
|
|
97
|
+
node.deref()
|
|
98
|
+
if options.verbose:
|
|
99
|
+
manager.print_stdout()
|
|
100
|
+
|
|
101
|
+
if options.output_sdd_filename is not None:
|
|
102
|
+
print("saving compiled sdd ...", end="")
|
|
103
|
+
node.save(options.output_sdd_filename)
|
|
104
|
+
print("done")
|
|
105
|
+
|
|
106
|
+
if options.output_sdd_dot_filename is not None:
|
|
107
|
+
print("saving compiled sdd (dot) ...", end="")
|
|
108
|
+
node.save_as_dot(options.output_sdd_dot_filename)
|
|
109
|
+
print("done")
|
|
110
|
+
|
|
111
|
+
if options.output_vtree_filename is not None:
|
|
112
|
+
print("saving vtree ...", end="")
|
|
113
|
+
manager_vtree.save(options.output_vtree_filename)
|
|
114
|
+
print("done")
|
|
115
|
+
|
|
116
|
+
if options.output_vtree_dot_filename is not None:
|
|
117
|
+
print("saving vtree (dot) ...", end="")
|
|
118
|
+
manager_vtree.save_as_dot(options.output_vtree_dot_filename)
|
|
119
|
+
print("done")
|
|
120
|
+
|
|
121
|
+
print("done")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def create_wmc(node, weights, args):
|
|
125
|
+
if weights is None:
|
|
126
|
+
return None
|
|
127
|
+
wmc = node.wmc(log_mode=args.log_mode)
|
|
128
|
+
for i in range(len(weights)):
|
|
129
|
+
lit = (i // 2) + 1
|
|
130
|
+
if (i % 2) == 1:
|
|
131
|
+
lit = -lit
|
|
132
|
+
w = weights[i]
|
|
133
|
+
wmc.set_literal_weight(node.manager.literal(lit), w)
|
|
134
|
+
return wmc
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def read_weights(nnf_path):
|
|
138
|
+
"""
|
|
139
|
+
Format: c weights PW_1 NW_1 ... PW_n NW_n
|
|
140
|
+
:param nnf_path: Path to NNF file
|
|
141
|
+
:return: list of weights
|
|
142
|
+
"""
|
|
143
|
+
weight_str = None
|
|
144
|
+
with open(nnf_path, "r") as ifile:
|
|
145
|
+
for line in ifile.readlines():
|
|
146
|
+
if "c weights " in line:
|
|
147
|
+
weight_str = line[10:].strip()
|
|
148
|
+
break
|
|
149
|
+
if weight_str is None:
|
|
150
|
+
return None
|
|
151
|
+
weight_strs = weight_str.split(" ")
|
|
152
|
+
weights = [float(w) for w in weight_strs]
|
|
153
|
+
return weights
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def print_node(node, wmc=None):
|
|
157
|
+
print(f" sdd size : {node.size()}")
|
|
158
|
+
print(f" sdd node count : {node.count()}")
|
|
159
|
+
c1 = time.time()
|
|
160
|
+
mc = node.global_model_count()
|
|
161
|
+
c2 = time.time()
|
|
162
|
+
print(f" sdd model count : {mc} {c2-c1:.3f} sec")
|
|
163
|
+
if wmc is not None:
|
|
164
|
+
c1 = time.time()
|
|
165
|
+
mc = wmc.propagate()
|
|
166
|
+
c2 = time.time()
|
|
167
|
+
print(f" sdd weighted model count: {mc} {c2-c1:.3f} sec")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class CustomHelpFormatter(argparse.HelpFormatter):
|
|
171
|
+
|
|
172
|
+
def _format_text(self, text):
|
|
173
|
+
text = super()._format_text(text)
|
|
174
|
+
if "Copyright" in text:
|
|
175
|
+
a, b = text.split("Copyright")
|
|
176
|
+
text = a + "\n\n" + super()._format_text("Copyright" + b)
|
|
177
|
+
return text
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def create_parser():
|
|
181
|
+
def bytes_path(p):
|
|
182
|
+
return bytes(Path(p))
|
|
183
|
+
|
|
184
|
+
def bytes_str(s):
|
|
185
|
+
return s.encode()
|
|
186
|
+
|
|
187
|
+
epilog = ("Weighted Model Counting is performed if the NNF file containts a line formatted as follows: "
|
|
188
|
+
"\"c weights PW_1 NW_1 ... PW_n NW_n\"."
|
|
189
|
+
"Copyright 2017-2019, Regents of the University of California and KU Leuven")
|
|
190
|
+
parser = argparse.ArgumentParser(description='Sentential Decision Diagram, Compiler',
|
|
191
|
+
epilog=epilog,
|
|
192
|
+
formatter_class=CustomHelpFormatter)
|
|
193
|
+
input_group = parser.add_mutually_exclusive_group()
|
|
194
|
+
input_group.add_argument('-c', metavar='FILE', type=bytes_path,
|
|
195
|
+
dest='cnf_filename', help='set input CNF file')
|
|
196
|
+
input_group.add_argument('-d', metavar='FILE', type=bytes_path,
|
|
197
|
+
dest='dnf_filename', help='set input DNF file')
|
|
198
|
+
input_group.add_argument('-s', metavar='FILE', type=bytes_path,
|
|
199
|
+
dest='sdd_filename', help='set input SDD file')
|
|
200
|
+
parser.add_argument('-v', metavar='FILE', type=bytes_path,
|
|
201
|
+
dest='vtree_filename', help='set input VTREE file')
|
|
202
|
+
|
|
203
|
+
parser.add_argument('-W', metavar='FILE', type=bytes_path,
|
|
204
|
+
dest='output_vtree_filename', help='set output VTREE file')
|
|
205
|
+
parser.add_argument('-V', metavar='FILE', type=bytes_path,
|
|
206
|
+
dest='output_vtree_dot_filename', help='set output VTREE (dot) file')
|
|
207
|
+
parser.add_argument('-R', metavar='FILE', type=bytes_path,
|
|
208
|
+
dest='output_sdd_filename', help='set output SDD file')
|
|
209
|
+
parser.add_argument('-S', metavar='FILE', type=bytes_path,
|
|
210
|
+
dest='output_sdd_dot_filename', help='set output SDD (dot) file')
|
|
211
|
+
|
|
212
|
+
parser.add_argument('-m', action='store_true',
|
|
213
|
+
dest='minimize_cardinality', help='minimize the cardinality of compiled sdd')
|
|
214
|
+
|
|
215
|
+
parser.add_argument('-t', metavar='TYPE', type=bytes_str, default='balanced',
|
|
216
|
+
choices=[b'left', b'right', b'vertical', b'balanced', b'random'],
|
|
217
|
+
dest='initial_vtree_type',
|
|
218
|
+
help='set initial vtree type (left/right/vertical/balanced/random)')
|
|
219
|
+
|
|
220
|
+
parser.add_argument('-r', metavar='K', type=int, default=-1,
|
|
221
|
+
dest='vtree_search_mode',
|
|
222
|
+
help='if K>0: invoke vtree search every K clauses. If K=0: disable vtree search. ' +
|
|
223
|
+
'By default (no -r option), dynamic vtree search is enabled')
|
|
224
|
+
|
|
225
|
+
parser.add_argument('-q', action='store_true',
|
|
226
|
+
dest='post_search', help='perform post-compilation vtree search')
|
|
227
|
+
|
|
228
|
+
parser.add_argument('-p', action='store_true',
|
|
229
|
+
dest='verbose', help='verbose output')
|
|
230
|
+
|
|
231
|
+
parser.add_argument('--log_mode', action='store_true',
|
|
232
|
+
dest='log_mode', help='weights in log')
|
|
233
|
+
|
|
234
|
+
return parser
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def getopt(argv=None):
|
|
238
|
+
parser = create_parser()
|
|
239
|
+
args = parser.parse_args(argv)
|
|
240
|
+
|
|
241
|
+
if args.verbose:
|
|
242
|
+
logger.setLevel(logging.INFO)
|
|
243
|
+
else:
|
|
244
|
+
logger.setLevel(logging.WARNING)
|
|
245
|
+
logger.addHandler(logging.StreamHandler(sys.stdout))
|
|
246
|
+
|
|
247
|
+
options = CompilerOptions(cnf_filename=args.cnf_filename,
|
|
248
|
+
dnf_filename=args.dnf_filename,
|
|
249
|
+
vtree_filename=args.vtree_filename,
|
|
250
|
+
sdd_filename=args.sdd_filename,
|
|
251
|
+
output_vtree_filename=args.output_vtree_filename,
|
|
252
|
+
output_vtree_dot_filename=args.output_vtree_dot_filename,
|
|
253
|
+
output_sdd_filename=args.output_sdd_filename,
|
|
254
|
+
output_sdd_dot_filename=args.output_sdd_dot_filename,
|
|
255
|
+
initial_vtree_type=args.initial_vtree_type,
|
|
256
|
+
minimize_cardinality=args.minimize_cardinality,
|
|
257
|
+
vtree_search_mode=args.vtree_search_mode,
|
|
258
|
+
post_search=args.post_search,
|
|
259
|
+
verbose=args.verbose)
|
|
260
|
+
# print(str(options))
|
|
261
|
+
return options, args
|
pysdd/compiler_c.pxd
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
compiler_c - SDD
|
|
3
|
+
|
|
4
|
+
__author__ = "Wannes Meert, Arthur Choi"
|
|
5
|
+
__license__ = "APL"
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from . cimport sddapi_c
|
|
10
|
+
|
|
11
|
+
cdef extern from "compiler.h":
|
|
12
|
+
ctypedef struct SddCompilerOptions:
|
|
13
|
+
# input files
|
|
14
|
+
char* cnf_filename # input cnf filename
|
|
15
|
+
char* dnf_filename # input dnf filename
|
|
16
|
+
char* vtree_filename # input vtree filename
|
|
17
|
+
char* sdd_filename # input sdd filename (.sdd, plain text)
|
|
18
|
+
# output files
|
|
19
|
+
char* output_vtree_filename # output vtree filename (.vtree, plain text)
|
|
20
|
+
char* output_vtree_dot_filename # output vtree filename (.dot)
|
|
21
|
+
char* output_sdd_filename # output sdd filename (.sdd, plain text)
|
|
22
|
+
char* output_sdd_dot_filename # output sdd filename (.dot)
|
|
23
|
+
# flags
|
|
24
|
+
int minimize_cardinality # construct sdd that has only minimum cardinality models
|
|
25
|
+
# initial vtree
|
|
26
|
+
char* initial_vtree_type # initial vtree for manager
|
|
27
|
+
# compilation controls
|
|
28
|
+
int vtree_search_mode # vtree search mode
|
|
29
|
+
int post_search # post-compilation search
|
|
30
|
+
int verbose # print manager
|
|
31
|
+
ctypedef struct Fnf:
|
|
32
|
+
long long var_count;
|
|
33
|
+
sddapi_c.SddSize litset_count;
|
|
34
|
+
ctypedef Fnf Cnf;
|
|
35
|
+
ctypedef Fnf Dnf;
|
|
36
|
+
|
|
37
|
+
sddapi_c.SddNode* fnf_to_sdd(Fnf* fnf, sddapi_c.SddManager* manager);
|
pysdd/fnf_c.pxd
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
pysdd.fnf_c
|
|
4
|
+
~~~~~~~~~~~
|
|
5
|
+
|
|
6
|
+
:author: Wannes Meert, Arthur Choi
|
|
7
|
+
:copyright: Copyright 2017-2018 KU Leuven and Regents of the University of California.
|
|
8
|
+
:license: Apache License, Version 2.0, see LICENSE for details.
|
|
9
|
+
"""
|
|
10
|
+
from . cimport sddapi_c
|
|
11
|
+
from . cimport compiler_c
|
|
12
|
+
|
|
13
|
+
cdef extern from "fnf.h":
|
|
14
|
+
void free_fnf(compiler_c.Fnf* fnf)
|
|
15
|
+
|
|
16
|
+
# cdef extern from "fnf_wrapper.h":
|
|
17
|
+
# void free_fnf_wrapper(compiler_c.Fnf* fnf)
|
pysdd/io_c.pxd
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
pysdd.io_c
|
|
4
|
+
~~~~~~~~~~
|
|
5
|
+
|
|
6
|
+
:author: Wannes Meert, Arthur Choi
|
|
7
|
+
:copyright: Copyright 2017-2018 KU Leuven and Regents of the University of California.
|
|
8
|
+
:license: Apache License, Version 2.0, see LICENSE for details.
|
|
9
|
+
"""
|
|
10
|
+
from . cimport sddapi_c
|
|
11
|
+
from . cimport compiler_c
|
|
12
|
+
|
|
13
|
+
cdef extern from "io.h":
|
|
14
|
+
compiler_c.Fnf* read_fnf(const char* filename);
|
|
15
|
+
compiler_c.Cnf* read_cnf(const char* filename);
|
|
16
|
+
compiler_c.Dnf* read_dnf(const char* filename);
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# cdef extern from "io_wrapper.h":
|
|
20
|
+
# compiler_c.Fnf* read_fnf_wrapper(const char* filename)
|
|
21
|
+
# compiler_c.Cnf* read_cnf_wrapper(const char* filename)
|
|
22
|
+
# compiler_c.Dnf* read_dnf_wrapper(const char* filename)
|
pysdd/iterator.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
pysdd.iterator
|
|
4
|
+
~~~~~~~~~~~~~~
|
|
5
|
+
|
|
6
|
+
:author: Wannes Meert, Arthur Choi
|
|
7
|
+
:copyright: Copyright 2017-2019 KU Leuven and Regents of the University of California.
|
|
8
|
+
:license: Apache License, Version 2.0, see LICENSE for details.
|
|
9
|
+
"""
|
|
10
|
+
from collections import deque
|
|
11
|
+
from .sdd import SddManager, Vtree, SddNode
|
|
12
|
+
from .util import BitArray
|
|
13
|
+
|
|
14
|
+
MYPY = False
|
|
15
|
+
if MYPY:
|
|
16
|
+
from typing import Dict, Set, Optional, List, Tuple, Callable, Union
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SddIterator:
|
|
20
|
+
def __init__(self, sdd, smooth=True, smooth_to_root=False):
|
|
21
|
+
"""Simple iterator to iterate over the SDD graph.
|
|
22
|
+
|
|
23
|
+
Supports smoothing: An arithmetic circuit AC(X) is smooth iff
|
|
24
|
+
(1) it contains at least one indicator for each variable in X, and
|
|
25
|
+
(2) for every child c of '+'-node n, we have vars(n) = vars(c).
|
|
26
|
+
|
|
27
|
+
Note, if you know that you will be performing WMC, smoothing can be implemented more efficient
|
|
28
|
+
by keeping track of the expected WMC of used and unused variables in the vtree instead of keeping
|
|
29
|
+
track of the sets of variables as is done in this iterator.
|
|
30
|
+
|
|
31
|
+
:param sdd: WmcManager
|
|
32
|
+
:param smooth: Perform smoothing while iterating over the graph
|
|
33
|
+
:param smooth_to_root: Perform smoothing wrt root instead of a given node
|
|
34
|
+
"""
|
|
35
|
+
self.sdd = sdd # type: SddManager
|
|
36
|
+
self.vtree = sdd.vtree() # type: Vtree
|
|
37
|
+
self._wmc_cache = dict() # type: Dict[SddNode, Union[float, int]]
|
|
38
|
+
# Map Vtree node positions to expected variables
|
|
39
|
+
self._expected_vars = None # type: Optional[Dict[int, Set[int]]]
|
|
40
|
+
# Map Sdd nodes to missing variables
|
|
41
|
+
self._missing_vars = dict() # type: Dict[SddNode, Set[int]]
|
|
42
|
+
self.smooth = smooth # type: bool
|
|
43
|
+
self.smooth_to_root = smooth_to_root # type: bool
|
|
44
|
+
|
|
45
|
+
if self.smooth:
|
|
46
|
+
self._cache_expected_vars()
|
|
47
|
+
|
|
48
|
+
def _cache_expected_vars(self):
|
|
49
|
+
self._expected_vars = dict()
|
|
50
|
+
nb_vtree_nodes = self.sdd.var_count() * 2 - 1
|
|
51
|
+
# visited = [False] * nb_vtree_nodes
|
|
52
|
+
visited = BitArray(nb_vtree_nodes)
|
|
53
|
+
queue = deque([self.vtree])
|
|
54
|
+
while len(queue) > 0:
|
|
55
|
+
node = queue.pop() # type: Vtree
|
|
56
|
+
pos = node.position()
|
|
57
|
+
if node.is_leaf():
|
|
58
|
+
self._expected_vars[pos] = {node.var()}
|
|
59
|
+
else:
|
|
60
|
+
if visited[pos]:
|
|
61
|
+
self._expected_vars[pos] = self._expected_vars[node.left().position()] | \
|
|
62
|
+
self._expected_vars[node.right().position()]
|
|
63
|
+
else:
|
|
64
|
+
visited[pos] = True
|
|
65
|
+
queue.append(node)
|
|
66
|
+
queue.append(node.right())
|
|
67
|
+
queue.append(node.left())
|
|
68
|
+
|
|
69
|
+
def depth_first_from_root(self, func):
|
|
70
|
+
# type: (SddIterator, Callable) -> List[Union[int, float]]
|
|
71
|
+
"""Depth first iterator starting from the root nodes.
|
|
72
|
+
|
|
73
|
+
See `depth_first` for more details.
|
|
74
|
+
"""
|
|
75
|
+
results = []
|
|
76
|
+
for node in self.vtree.get_sdd_rootnodes(self.sdd):
|
|
77
|
+
results.append(self.depth_first(node, func))
|
|
78
|
+
return results
|
|
79
|
+
|
|
80
|
+
def depth_first(self, node, func):
|
|
81
|
+
# type: (SddIterator, SddNode, Callable) -> Union[int, float]
|
|
82
|
+
"""Depth first iterator
|
|
83
|
+
|
|
84
|
+
:param node: Start node
|
|
85
|
+
:param func: Function to be called for each node:
|
|
86
|
+
``rvalue = func(node, [(prime_rvalue, sub_rvalue, prime_vars, sub_vars)],
|
|
87
|
+
expected_prime_vars, expected_sub_vars)``
|
|
88
|
+
The given arguments are:
|
|
89
|
+
* node: Current node, if None, it should be considered a decision node
|
|
90
|
+
* prime_rvalue: Return value from prime node
|
|
91
|
+
* sub_rvalue: Return value from sub node
|
|
92
|
+
* prime_vars: Variables present in prime
|
|
93
|
+
* sub_vars: Variables present in sub
|
|
94
|
+
* expected_prime_vars: Variables that are expected in prime
|
|
95
|
+
* expected_sub_vars: Variables that are expected in sub
|
|
96
|
+
The return value can be any type, there are no assumptions.
|
|
97
|
+
For WMC this is typically float, and for MC int.
|
|
98
|
+
:return:
|
|
99
|
+
"""
|
|
100
|
+
self._wmc_cache = dict()
|
|
101
|
+
if self.smooth and self._expected_vars is None:
|
|
102
|
+
self._cache_expected_vars()
|
|
103
|
+
if self.smooth and (node.is_true() or node.is_literal()):
|
|
104
|
+
wmc = func(node, None, self._expected_vars[self.vtree.position()], set())
|
|
105
|
+
else:
|
|
106
|
+
wmc = self.depth_first_rec(node, func)
|
|
107
|
+
if self.smooth_to_root and not (node.is_true() or node.is_literal() or node.is_false()):
|
|
108
|
+
root = self.vtree.root()
|
|
109
|
+
if root != node.vtree():
|
|
110
|
+
wmc_prime = wmc
|
|
111
|
+
wmc_sub = func(self.sdd.true(), None, None, None)
|
|
112
|
+
used_prime_vars = self._expected_vars[node.vtree().position()]
|
|
113
|
+
used_sub_vars = set()
|
|
114
|
+
rvalues = [(wmc_prime, wmc_sub, used_prime_vars, used_sub_vars)]
|
|
115
|
+
expected_prime_vars = used_prime_vars
|
|
116
|
+
expected_sub_vars = self._expected_vars[root.position()] - used_prime_vars
|
|
117
|
+
wmc = func(None, rvalues, expected_prime_vars, expected_sub_vars)
|
|
118
|
+
return wmc
|
|
119
|
+
|
|
120
|
+
def depth_first_rec(self, node, func):
|
|
121
|
+
# type: (SddIterator, SddNode, Callable) -> Union[int, float]
|
|
122
|
+
if node in self._wmc_cache:
|
|
123
|
+
return self._wmc_cache[node]
|
|
124
|
+
if node.is_false():
|
|
125
|
+
result = func(node, None, None, None)
|
|
126
|
+
self._wmc_cache[node] = result
|
|
127
|
+
return result
|
|
128
|
+
if node.is_true():
|
|
129
|
+
result = func(node, None, None, None)
|
|
130
|
+
self._wmc_cache[node] = result
|
|
131
|
+
return result
|
|
132
|
+
vtree = node.vtree()
|
|
133
|
+
if node.is_decision():
|
|
134
|
+
rvalues = []
|
|
135
|
+
vtree_left = vtree.left()
|
|
136
|
+
if not self.smooth or vtree_left is None:
|
|
137
|
+
expected_prime_vars = set()
|
|
138
|
+
else:
|
|
139
|
+
expected_prime_vars = self._expected_vars[vtree_left.position()]
|
|
140
|
+
vtree_right = vtree.right()
|
|
141
|
+
if not self.smooth or vtree_right is None:
|
|
142
|
+
expected_sub_vars = set()
|
|
143
|
+
else:
|
|
144
|
+
expected_sub_vars = self._expected_vars[vtree_right.position()]
|
|
145
|
+
for prime, sub in node.elements():
|
|
146
|
+
wmc_prime = self.depth_first_rec(prime, func)
|
|
147
|
+
wmc_sub = self.depth_first_rec(sub, func)
|
|
148
|
+
if not self.smooth:
|
|
149
|
+
used_prime_vars = None
|
|
150
|
+
used_sub_vars = None
|
|
151
|
+
else:
|
|
152
|
+
if prime.vtree() is None:
|
|
153
|
+
used_prime_vars = set()
|
|
154
|
+
else:
|
|
155
|
+
used_prime_vars = self._expected_vars[prime.vtree().position()]
|
|
156
|
+
if sub.vtree() is None:
|
|
157
|
+
used_sub_vars = set()
|
|
158
|
+
else:
|
|
159
|
+
used_sub_vars = self._expected_vars[sub.vtree().position()]
|
|
160
|
+
rvalues.append((wmc_prime, wmc_sub, used_prime_vars, used_sub_vars))
|
|
161
|
+
rvalue = func(node, rvalues, expected_prime_vars, expected_sub_vars)
|
|
162
|
+
else:
|
|
163
|
+
rvalue = func(node, None, None, None)
|
|
164
|
+
if self._wmc_cache is not None:
|
|
165
|
+
self._wmc_cache[node] = rvalue
|
|
166
|
+
return rvalue
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def func_modelcounting(node, rvalues, expected_prime_vars, expected_sub_vars):
|
|
170
|
+
# type: (SddNode, List[Tuple[int, int, Set[int], Set[int]]], Set[int], Set[int]) -> int
|
|
171
|
+
"""Method to pass on to ``depth_first`` to perform model counting.
|
|
172
|
+
|
|
173
|
+
Note that the WmcManager method to perform WMC is much more efficient, and supports weights.
|
|
174
|
+
"""
|
|
175
|
+
if rvalues is None:
|
|
176
|
+
# Leaf
|
|
177
|
+
if node.is_true():
|
|
178
|
+
prime_smooth_factor = 2**len(expected_prime_vars) if expected_prime_vars is not None else 1
|
|
179
|
+
sub_smooth_factor = 2**len(expected_sub_vars) if expected_sub_vars is not None else 1
|
|
180
|
+
return prime_smooth_factor * sub_smooth_factor
|
|
181
|
+
|
|
182
|
+
elif node.is_false():
|
|
183
|
+
return 0
|
|
184
|
+
|
|
185
|
+
elif node.is_literal():
|
|
186
|
+
if expected_prime_vars is not None:
|
|
187
|
+
nb_missing_vars = len(expected_prime_vars)
|
|
188
|
+
if abs(node.literal) in expected_prime_vars:
|
|
189
|
+
nb_missing_vars -= 1
|
|
190
|
+
|
|
191
|
+
prime_smooth_factor = 2**nb_missing_vars
|
|
192
|
+
else:
|
|
193
|
+
prime_smooth_factor = 1
|
|
194
|
+
|
|
195
|
+
if expected_sub_vars is not None:
|
|
196
|
+
nb_missing_vars = len(expected_sub_vars)
|
|
197
|
+
if abs(node.literal) in expected_sub_vars:
|
|
198
|
+
nb_missing_vars -= 1
|
|
199
|
+
sub_smooth_factor = 2**nb_missing_vars
|
|
200
|
+
else:
|
|
201
|
+
sub_smooth_factor = 1
|
|
202
|
+
|
|
203
|
+
return prime_smooth_factor * sub_smooth_factor
|
|
204
|
+
else:
|
|
205
|
+
raise Exception("Unknown leaf type for node {}".format(node))
|
|
206
|
+
else:
|
|
207
|
+
# Decision node
|
|
208
|
+
if node is not None and not node.is_decision():
|
|
209
|
+
raise Exception("Expected a decision node for node {}".format(node))
|
|
210
|
+
rvalue = 0
|
|
211
|
+
for mc_prime, mc_sub, prime_vars, sub_vars in rvalues:
|
|
212
|
+
if prime_vars is not None:
|
|
213
|
+
nb_missing_vars = len(expected_prime_vars) - len(prime_vars)
|
|
214
|
+
prime_smooth_factor = 2**nb_missing_vars
|
|
215
|
+
else:
|
|
216
|
+
prime_smooth_factor = 1
|
|
217
|
+
if sub_vars is not None:
|
|
218
|
+
nb_missing_vars = len(expected_sub_vars) - len(sub_vars)
|
|
219
|
+
sub_smooth_factor = 2**nb_missing_vars
|
|
220
|
+
else:
|
|
221
|
+
sub_smooth_factor = 1
|
|
222
|
+
rvalue += mc_prime * prime_smooth_factor * mc_sub * sub_smooth_factor
|
|
223
|
+
return rvalue
|