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.
Files changed (90) hide show
  1. pysdd/__init__.py +1 -0
  2. pysdd/__main__.py +19 -0
  3. pysdd/cli.py +261 -0
  4. pysdd/compiler_c.pxd +37 -0
  5. pysdd/fnf_c.pxd +17 -0
  6. pysdd/io_c.pxd +22 -0
  7. pysdd/iterator.py +223 -0
  8. pysdd/lib/libsdd-2.0/include/iterators.h +243 -0
  9. pysdd/lib/libsdd-2.0/include/macros.h +273 -0
  10. pysdd/lib/libsdd-2.0/include/parameters.h +71 -0
  11. pysdd/lib/libsdd-2.0/include/sdd.h +823 -0
  12. pysdd/lib/libsdd-2.0/include/stacks.h +86 -0
  13. pysdd/lib/libsdd-2.0/src/basic/computed.c +91 -0
  14. pysdd/lib/libsdd-2.0/src/basic/count_and_size.c +173 -0
  15. pysdd/lib/libsdd-2.0/src/basic/gc.c +104 -0
  16. pysdd/lib/libsdd-2.0/src/basic/hash.c +221 -0
  17. pysdd/lib/libsdd-2.0/src/basic/memory.c +180 -0
  18. pysdd/lib/libsdd-2.0/src/basic/multiply.c +209 -0
  19. pysdd/lib/libsdd-2.0/src/basic/nodes.c +174 -0
  20. pysdd/lib/libsdd-2.0/src/basic/partitions.c +351 -0
  21. pysdd/lib/libsdd-2.0/src/basic/references.c +79 -0
  22. pysdd/lib/libsdd-2.0/src/basic/replace.c +91 -0
  23. pysdd/lib/libsdd-2.0/src/basic/shadows.c +384 -0
  24. pysdd/lib/libsdd-2.0/src/basic/sort.c +148 -0
  25. pysdd/lib/libsdd-2.0/src/manager/copy.c +84 -0
  26. pysdd/lib/libsdd-2.0/src/manager/interface.c +411 -0
  27. pysdd/lib/libsdd-2.0/src/manager/manager.c +308 -0
  28. pysdd/lib/libsdd-2.0/src/manager/stats.c +121 -0
  29. pysdd/lib/libsdd-2.0/src/manager/variables.c +267 -0
  30. pysdd/lib/libsdd-2.0/src/sdds/apply.c +396 -0
  31. pysdd/lib/libsdd-2.0/src/sdds/bits.c +156 -0
  32. pysdd/lib/libsdd-2.0/src/sdds/cardinality.c +313 -0
  33. pysdd/lib/libsdd-2.0/src/sdds/condition.c +107 -0
  34. pysdd/lib/libsdd-2.0/src/sdds/copy.c +117 -0
  35. pysdd/lib/libsdd-2.0/src/sdds/count.c +78 -0
  36. pysdd/lib/libsdd-2.0/src/sdds/essential_vars.c +128 -0
  37. pysdd/lib/libsdd-2.0/src/sdds/exists.c +29 -0
  38. pysdd/lib/libsdd-2.0/src/sdds/exists_multiple.c +311 -0
  39. pysdd/lib/libsdd-2.0/src/sdds/exists_multiple_static.c +165 -0
  40. pysdd/lib/libsdd-2.0/src/sdds/forall.c +28 -0
  41. pysdd/lib/libsdd-2.0/src/sdds/io.c +448 -0
  42. pysdd/lib/libsdd-2.0/src/sdds/model_count.c +146 -0
  43. pysdd/lib/libsdd-2.0/src/sdds/rename_vars.c +113 -0
  44. pysdd/lib/libsdd-2.0/src/sdds/size.c +65 -0
  45. pysdd/lib/libsdd-2.0/src/sdds/wmc.c +412 -0
  46. pysdd/lib/libsdd-2.0/src/util.c +255 -0
  47. pysdd/lib/libsdd-2.0/src/verify.c +236 -0
  48. pysdd/lib/libsdd-2.0/src/version.c +14 -0
  49. pysdd/lib/libsdd-2.0/src/vtree_fragments/construction.c +329 -0
  50. pysdd/lib/libsdd-2.0/src/vtree_fragments/moves.c +100 -0
  51. pysdd/lib/libsdd-2.0/src/vtree_fragments/operations.c +302 -0
  52. pysdd/lib/libsdd-2.0/src/vtree_operations/cartesian_product.c +166 -0
  53. pysdd/lib/libsdd-2.0/src/vtree_operations/dissect.c +78 -0
  54. pysdd/lib/libsdd-2.0/src/vtree_operations/limits.c +237 -0
  55. pysdd/lib/libsdd-2.0/src/vtree_operations/op_left_rotate.c +143 -0
  56. pysdd/lib/libsdd-2.0/src/vtree_operations/op_right_rotate.c +150 -0
  57. pysdd/lib/libsdd-2.0/src/vtree_operations/op_swap.c +112 -0
  58. pysdd/lib/libsdd-2.0/src/vtree_operations/rollback.c +68 -0
  59. pysdd/lib/libsdd-2.0/src/vtree_operations/split.c +180 -0
  60. pysdd/lib/libsdd-2.0/src/vtree_operations/vtree_ops.h +48 -0
  61. pysdd/lib/libsdd-2.0/src/vtree_search/auto.c +186 -0
  62. pysdd/lib/libsdd-2.0/src/vtree_search/search.c +332 -0
  63. pysdd/lib/libsdd-2.0/src/vtree_search/state.c +127 -0
  64. pysdd/lib/libsdd-2.0/src/vtrees/compare.c +138 -0
  65. pysdd/lib/libsdd-2.0/src/vtrees/edit.c +199 -0
  66. pysdd/lib/libsdd-2.0/src/vtrees/io.c +207 -0
  67. pysdd/lib/libsdd-2.0/src/vtrees/maps.c +42 -0
  68. pysdd/lib/libsdd-2.0/src/vtrees/moves.c +156 -0
  69. pysdd/lib/libsdd-2.0/src/vtrees/static.c +270 -0
  70. pysdd/lib/libsdd-2.0/src/vtrees/vtree.c +197 -0
  71. pysdd/lib/sdd-2.0/include/compiler.h +104 -0
  72. pysdd/lib/sdd-2.0/include/fnf.h +13 -0
  73. pysdd/lib/sdd-2.0/include/io.h +11 -0
  74. pysdd/lib/sdd-2.0/include/sddapi.h +341 -0
  75. pysdd/lib/sdd-2.0/src/fnf/compiler.c +120 -0
  76. pysdd/lib/sdd-2.0/src/fnf/fnf.c +57 -0
  77. pysdd/lib/sdd-2.0/src/fnf/io.c +201 -0
  78. pysdd/lib/sdd-2.0/src/fnf/utils.c +72 -0
  79. pysdd/lib/sdd_extra/include/sddapi_extra.h +22 -0
  80. pysdd/sdd.cp315-win_amd64.pyd +0 -0
  81. pysdd/sdd.pyx +1808 -0
  82. pysdd/sddapi_c.pxd +241 -0
  83. pysdd/util.py +450 -0
  84. pysdd/wmcstochastic.py +188 -0
  85. pysdd-1.0.7.dist-info/METADATA +216 -0
  86. pysdd-1.0.7.dist-info/RECORD +90 -0
  87. pysdd-1.0.7.dist-info/WHEEL +5 -0
  88. pysdd-1.0.7.dist-info/entry_points.txt +2 -0
  89. pysdd-1.0.7.dist-info/licenses/LICENSE +13 -0
  90. 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