PySDD 1.0.6__cp314-cp314-win32.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 (75) hide show
  1. pysdd/__init__.py +1 -0
  2. pysdd/__main__.py +19 -0
  3. pysdd/cli.py +261 -0
  4. pysdd/iterator.py +223 -0
  5. pysdd/lib/libsdd-2.0/src/basic/computed.c +91 -0
  6. pysdd/lib/libsdd-2.0/src/basic/count_and_size.c +173 -0
  7. pysdd/lib/libsdd-2.0/src/basic/gc.c +104 -0
  8. pysdd/lib/libsdd-2.0/src/basic/hash.c +221 -0
  9. pysdd/lib/libsdd-2.0/src/basic/memory.c +180 -0
  10. pysdd/lib/libsdd-2.0/src/basic/multiply.c +209 -0
  11. pysdd/lib/libsdd-2.0/src/basic/nodes.c +174 -0
  12. pysdd/lib/libsdd-2.0/src/basic/partitions.c +351 -0
  13. pysdd/lib/libsdd-2.0/src/basic/references.c +79 -0
  14. pysdd/lib/libsdd-2.0/src/basic/replace.c +91 -0
  15. pysdd/lib/libsdd-2.0/src/basic/shadows.c +384 -0
  16. pysdd/lib/libsdd-2.0/src/basic/sort.c +148 -0
  17. pysdd/lib/libsdd-2.0/src/manager/copy.c +84 -0
  18. pysdd/lib/libsdd-2.0/src/manager/interface.c +411 -0
  19. pysdd/lib/libsdd-2.0/src/manager/manager.c +308 -0
  20. pysdd/lib/libsdd-2.0/src/manager/stats.c +121 -0
  21. pysdd/lib/libsdd-2.0/src/manager/variables.c +267 -0
  22. pysdd/lib/libsdd-2.0/src/sdds/apply.c +396 -0
  23. pysdd/lib/libsdd-2.0/src/sdds/bits.c +156 -0
  24. pysdd/lib/libsdd-2.0/src/sdds/cardinality.c +313 -0
  25. pysdd/lib/libsdd-2.0/src/sdds/condition.c +107 -0
  26. pysdd/lib/libsdd-2.0/src/sdds/copy.c +117 -0
  27. pysdd/lib/libsdd-2.0/src/sdds/count.c +78 -0
  28. pysdd/lib/libsdd-2.0/src/sdds/essential_vars.c +128 -0
  29. pysdd/lib/libsdd-2.0/src/sdds/exists.c +29 -0
  30. pysdd/lib/libsdd-2.0/src/sdds/exists_multiple.c +311 -0
  31. pysdd/lib/libsdd-2.0/src/sdds/exists_multiple_static.c +165 -0
  32. pysdd/lib/libsdd-2.0/src/sdds/forall.c +28 -0
  33. pysdd/lib/libsdd-2.0/src/sdds/io.c +448 -0
  34. pysdd/lib/libsdd-2.0/src/sdds/model_count.c +146 -0
  35. pysdd/lib/libsdd-2.0/src/sdds/rename_vars.c +113 -0
  36. pysdd/lib/libsdd-2.0/src/sdds/size.c +65 -0
  37. pysdd/lib/libsdd-2.0/src/sdds/wmc.c +412 -0
  38. pysdd/lib/libsdd-2.0/src/util.c +255 -0
  39. pysdd/lib/libsdd-2.0/src/verify.c +236 -0
  40. pysdd/lib/libsdd-2.0/src/version.c +14 -0
  41. pysdd/lib/libsdd-2.0/src/vtree_fragments/construction.c +329 -0
  42. pysdd/lib/libsdd-2.0/src/vtree_fragments/moves.c +100 -0
  43. pysdd/lib/libsdd-2.0/src/vtree_fragments/operations.c +302 -0
  44. pysdd/lib/libsdd-2.0/src/vtree_operations/cartesian_product.c +166 -0
  45. pysdd/lib/libsdd-2.0/src/vtree_operations/dissect.c +78 -0
  46. pysdd/lib/libsdd-2.0/src/vtree_operations/limits.c +237 -0
  47. pysdd/lib/libsdd-2.0/src/vtree_operations/op_left_rotate.c +143 -0
  48. pysdd/lib/libsdd-2.0/src/vtree_operations/op_right_rotate.c +150 -0
  49. pysdd/lib/libsdd-2.0/src/vtree_operations/op_swap.c +112 -0
  50. pysdd/lib/libsdd-2.0/src/vtree_operations/rollback.c +68 -0
  51. pysdd/lib/libsdd-2.0/src/vtree_operations/split.c +180 -0
  52. pysdd/lib/libsdd-2.0/src/vtree_search/auto.c +186 -0
  53. pysdd/lib/libsdd-2.0/src/vtree_search/search.c +332 -0
  54. pysdd/lib/libsdd-2.0/src/vtree_search/state.c +127 -0
  55. pysdd/lib/libsdd-2.0/src/vtrees/compare.c +138 -0
  56. pysdd/lib/libsdd-2.0/src/vtrees/edit.c +199 -0
  57. pysdd/lib/libsdd-2.0/src/vtrees/io.c +207 -0
  58. pysdd/lib/libsdd-2.0/src/vtrees/maps.c +42 -0
  59. pysdd/lib/libsdd-2.0/src/vtrees/moves.c +156 -0
  60. pysdd/lib/libsdd-2.0/src/vtrees/static.c +270 -0
  61. pysdd/lib/libsdd-2.0/src/vtrees/vtree.c +197 -0
  62. pysdd/lib/sdd-2.0/src/fnf/compiler.c +120 -0
  63. pysdd/lib/sdd-2.0/src/fnf/fnf.c +57 -0
  64. pysdd/lib/sdd-2.0/src/fnf/io.c +201 -0
  65. pysdd/lib/sdd-2.0/src/fnf/utils.c +72 -0
  66. pysdd/sdd.cp314-win32.pyd +0 -0
  67. pysdd/sdd.pyx +1808 -0
  68. pysdd/util.py +450 -0
  69. pysdd/wmcstochastic.py +188 -0
  70. pysdd-1.0.6.dist-info/METADATA +216 -0
  71. pysdd-1.0.6.dist-info/RECORD +75 -0
  72. pysdd-1.0.6.dist-info/WHEEL +5 -0
  73. pysdd-1.0.6.dist-info/entry_points.txt +2 -0
  74. pysdd-1.0.6.dist-info/licenses/LICENSE +13 -0
  75. pysdd-1.0.6.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/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
@@ -0,0 +1,91 @@
1
+ /****************************************************************************************
2
+ * The Sentential Decision Diagram Package
3
+ * sdd version 2.0, January 8, 2018
4
+ * http://reasoning.cs.ucla.edu/sdd
5
+ ****************************************************************************************/
6
+
7
+ #include "sdd.h"
8
+
9
+ /****************************************************************************************
10
+ * computation cache
11
+ *
12
+ * there are two hash tables for caching computations, one for conjoin and one for disjoin
13
+ *
14
+ * there are no collision lists in these hash tables: just one entry (similar to CUDD)
15
+ *
16
+ * a cached computation may become invalid when one of its nodes is gc'd; such invalid
17
+ * entries are identified in a lazy manner.
18
+ *
19
+ * a node whose id is 0 must have been gc'd and is sitting on the gc list
20
+ * however, a node whose id is not 0 may have also been gc'd and its structure re-allocated
21
+ *
22
+ * ids of nodes are monotonically increasing: if a node is gc'd and then re-allocated,
23
+ * its new id will be larger than the old one. moreover, id=0 is reserved for nodes
24
+ * on the gc list. hence, if the id of a node is different than the one saved, then
25
+ * the node must have been gc'd
26
+ ****************************************************************************************/
27
+
28
+ static inline
29
+ void delete_computed(SddComputed* computed, SddManager* manager) {
30
+ --manager->computed_count;
31
+ computed->result = NULL; //computed now deleted
32
+ }
33
+
34
+ static inline
35
+ SddSize hash_key(SddNode* node1, SddNode* node2) {
36
+ return ((16777619*node1->id)^(node2->id)) % COMPUTED_CACHE_SIZE;
37
+ }
38
+
39
+ /****************************************************************************************
40
+ * save
41
+ ****************************************************************************************/
42
+
43
+ void cache_computation(SddNode* node1, SddNode* node2, SddNode* node, BoolOp op, SddManager* manager) {
44
+ assert(!GC_NODE(node) && !GC_NODE(node1) && !GC_NODE(node2));
45
+ assert(NON_TRIVIAL(node1) && NON_TRIVIAL(node2));
46
+
47
+ if(node1->id > node2->id) SWAP(SddNode*,node1,node2); //for hash key
48
+
49
+ SddSize key = hash_key(node1,node2);
50
+ SddComputed* table = op==CONJOIN? manager->conjoin_cache: manager->disjoin_cache;
51
+ SddComputed* computed = table+key;
52
+
53
+ if(computed->result!=NULL) { //override current computed
54
+ delete_computed(computed,manager);
55
+ }
56
+
57
+ ++manager->computed_count;
58
+
59
+ computed->result = node;
60
+ computed->id = node->id; //needed to check whether result has been gc'd
61
+ computed->id1 = node1->id; //only id is needed for lookup
62
+ computed->id2 = node2->id; //only id is needed for lookup
63
+ }
64
+
65
+ /****************************************************************************************
66
+ * lookup
67
+ ****************************************************************************************/
68
+
69
+ SddNode* lookup_computation(SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager) {
70
+ assert(!GC_NODE(node1) && !GC_NODE(node2));
71
+ assert(NON_TRIVIAL(node1) && NON_TRIVIAL(node2));
72
+
73
+ if(node1->id > node2->id) SWAP(SddNode*,node1,node2); //for hash key and ordered comparison
74
+
75
+ SddSize key = hash_key(node1,node2);
76
+ SddComputed* table = op==CONJOIN? manager->conjoin_cache: manager->disjoin_cache;
77
+ SddComputed* computed = table+key;
78
+
79
+ ++manager->computed_cache_lookup_count;
80
+
81
+ if(computed->result==NULL || computed->id!=computed->result->id) return NULL; //missing or invalid computed
82
+ else if(computed->id1==node1->id && computed->id2==node2->id) {
83
+ ++manager->computed_cache_hit_count; //found it
84
+ return computed->result; //hit
85
+ }
86
+ else return NULL; //miss: non-matching computed for this key
87
+ }
88
+
89
+ /****************************************************************************************
90
+ * end
91
+ ****************************************************************************************/