binary-decision-diagram 3.2.0 → 5.0.0

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 (192) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +211 -31
  3. package/dist/cjs/src/abstract-node.js +7 -4
  4. package/dist/cjs/src/abstract-node.js.map +1 -1
  5. package/dist/cjs/src/branches.d.ts +4 -1
  6. package/dist/cjs/src/branches.js +10 -9
  7. package/dist/cjs/src/branches.js.map +1 -1
  8. package/dist/cjs/src/create-bdd-from-truth-table.js +1 -2
  9. package/dist/cjs/src/create-bdd-from-truth-table.js.map +1 -1
  10. package/dist/cjs/src/ensure-correct-bdd.js +2 -3
  11. package/dist/cjs/src/ensure-correct-bdd.js.map +1 -1
  12. package/dist/cjs/src/fill-truth-table.js +1 -2
  13. package/dist/cjs/src/fill-truth-table.js.map +1 -1
  14. package/dist/cjs/src/find-similar-node.js +1 -2
  15. package/dist/cjs/src/find-similar-node.js.map +1 -1
  16. package/dist/cjs/src/index.d.ts +2 -1
  17. package/dist/cjs/src/index.es5.js +17 -7
  18. package/dist/cjs/src/index.es5.js.map +1 -1
  19. package/dist/cjs/src/index.js +2 -1
  20. package/dist/cjs/src/index.js.map +1 -1
  21. package/dist/cjs/src/internal-node.js +6 -3
  22. package/dist/cjs/src/internal-node.js.map +1 -1
  23. package/dist/cjs/src/optimize-brute-force.js +10 -12
  24. package/dist/cjs/src/optimize-brute-force.js.map +1 -1
  25. package/dist/cjs/src/ordering/build-bdd.d.ts +13 -0
  26. package/dist/cjs/src/ordering/build-bdd.js +64 -0
  27. package/dist/cjs/src/ordering/build-bdd.js.map +1 -0
  28. package/dist/cjs/src/ordering/dense-truth-table.d.ts +42 -0
  29. package/dist/cjs/src/ordering/dense-truth-table.js +125 -0
  30. package/dist/cjs/src/ordering/dense-truth-table.js.map +1 -0
  31. package/dist/cjs/src/ordering/exact-cost-ordering.d.ts +91 -0
  32. package/dist/cjs/src/ordering/exact-cost-ordering.js +351 -0
  33. package/dist/cjs/src/ordering/exact-cost-ordering.js.map +1 -0
  34. package/dist/cjs/src/ordering/exact-ordering.d.ts +89 -0
  35. package/dist/cjs/src/ordering/exact-ordering.js +260 -0
  36. package/dist/cjs/src/ordering/exact-ordering.js.map +1 -0
  37. package/dist/cjs/src/ordering/index.d.ts +22 -0
  38. package/dist/cjs/src/ordering/index.js +38 -0
  39. package/dist/cjs/src/ordering/index.js.map +1 -0
  40. package/dist/cjs/src/ordering/int-pair-map.d.ts +20 -0
  41. package/dist/cjs/src/ordering/int-pair-map.js +85 -0
  42. package/dist/cjs/src/ordering/int-pair-map.js.map +1 -0
  43. package/dist/cjs/src/ordering/local-search.d.ts +114 -0
  44. package/dist/cjs/src/ordering/local-search.js +317 -0
  45. package/dist/cjs/src/ordering/local-search.js.map +1 -0
  46. package/dist/cjs/src/ordering/optimize-ordering.d.ts +153 -0
  47. package/dist/cjs/src/ordering/optimize-ordering.js +406 -0
  48. package/dist/cjs/src/ordering/optimize-ordering.js.map +1 -0
  49. package/dist/cjs/src/ordering/ordering-evaluator.d.ts +198 -0
  50. package/dist/cjs/src/ordering/ordering-evaluator.js +384 -0
  51. package/dist/cjs/src/ordering/ordering-evaluator.js.map +1 -0
  52. package/dist/cjs/src/ordering/shared-diagram.d.ts +85 -0
  53. package/dist/cjs/src/ordering/shared-diagram.js +295 -0
  54. package/dist/cjs/src/ordering/shared-diagram.js.map +1 -0
  55. package/dist/cjs/src/packed/index.d.ts +2 -0
  56. package/dist/cjs/src/{minimal-string → packed}/index.js +2 -5
  57. package/dist/cjs/src/packed/index.js.map +1 -0
  58. package/dist/cjs/src/packed/packed-bdd-base64-string.d.ts +3 -0
  59. package/dist/cjs/src/packed/packed-bdd-base64-string.js +46 -0
  60. package/dist/cjs/src/packed/packed-bdd-base64-string.js.map +1 -0
  61. package/dist/cjs/src/packed/packed-bdd.d.ts +13 -0
  62. package/dist/cjs/src/packed/packed-bdd.js +70 -0
  63. package/dist/cjs/src/packed/packed-bdd.js.map +1 -0
  64. package/dist/cjs/src/root-node.d.ts +1 -2
  65. package/dist/cjs/src/root-node.js +4 -9
  66. package/dist/cjs/src/root-node.js.map +1 -1
  67. package/dist/cjs/src/types.d.ts +0 -12
  68. package/dist/cjs/src/util.js +29 -21
  69. package/dist/cjs/src/util.js.map +1 -1
  70. package/dist/esm/src/abstract-node.js +7 -4
  71. package/dist/esm/src/abstract-node.js.map +1 -1
  72. package/dist/esm/src/branches.d.ts +4 -1
  73. package/dist/esm/src/branches.js +8 -7
  74. package/dist/esm/src/branches.js.map +1 -1
  75. package/dist/esm/src/create-bdd-from-truth-table.js.map +1 -1
  76. package/dist/esm/src/index.d.ts +2 -1
  77. package/dist/esm/src/index.js +2 -1
  78. package/dist/esm/src/index.js.map +1 -1
  79. package/dist/esm/src/internal-node.js +7 -4
  80. package/dist/esm/src/internal-node.js.map +1 -1
  81. package/dist/esm/src/optimize-brute-force.js +5 -7
  82. package/dist/esm/src/optimize-brute-force.js.map +1 -1
  83. package/dist/esm/src/ordering/build-bdd.d.ts +13 -0
  84. package/dist/esm/src/ordering/build-bdd.js +61 -0
  85. package/dist/esm/src/ordering/build-bdd.js.map +1 -0
  86. package/dist/esm/src/ordering/dense-truth-table.d.ts +42 -0
  87. package/dist/esm/src/ordering/dense-truth-table.js +117 -0
  88. package/dist/esm/src/ordering/dense-truth-table.js.map +1 -0
  89. package/dist/esm/src/ordering/exact-cost-ordering.d.ts +91 -0
  90. package/dist/esm/src/ordering/exact-cost-ordering.js +347 -0
  91. package/dist/esm/src/ordering/exact-cost-ordering.js.map +1 -0
  92. package/dist/esm/src/ordering/exact-ordering.d.ts +89 -0
  93. package/dist/esm/src/ordering/exact-ordering.js +256 -0
  94. package/dist/esm/src/ordering/exact-ordering.js.map +1 -0
  95. package/dist/esm/src/ordering/index.d.ts +22 -0
  96. package/dist/esm/src/ordering/index.js +17 -0
  97. package/dist/esm/src/ordering/index.js.map +1 -0
  98. package/dist/esm/src/ordering/int-pair-map.d.ts +20 -0
  99. package/dist/esm/src/ordering/int-pair-map.js +81 -0
  100. package/dist/esm/src/ordering/int-pair-map.js.map +1 -0
  101. package/dist/esm/src/ordering/local-search.d.ts +114 -0
  102. package/dist/esm/src/ordering/local-search.js +308 -0
  103. package/dist/esm/src/ordering/local-search.js.map +1 -0
  104. package/dist/esm/src/ordering/optimize-ordering.d.ts +153 -0
  105. package/dist/esm/src/ordering/optimize-ordering.js +399 -0
  106. package/dist/esm/src/ordering/optimize-ordering.js.map +1 -0
  107. package/dist/esm/src/ordering/ordering-evaluator.d.ts +198 -0
  108. package/dist/esm/src/ordering/ordering-evaluator.js +377 -0
  109. package/dist/esm/src/ordering/ordering-evaluator.js.map +1 -0
  110. package/dist/esm/src/ordering/shared-diagram.d.ts +85 -0
  111. package/dist/esm/src/ordering/shared-diagram.js +289 -0
  112. package/dist/esm/src/ordering/shared-diagram.js.map +1 -0
  113. package/dist/esm/src/packed/index.d.ts +2 -0
  114. package/dist/esm/src/packed/index.js +3 -0
  115. package/dist/esm/src/packed/index.js.map +1 -0
  116. package/dist/esm/src/packed/packed-bdd-base64-string.d.ts +3 -0
  117. package/dist/esm/src/packed/packed-bdd-base64-string.js +42 -0
  118. package/dist/esm/src/packed/packed-bdd-base64-string.js.map +1 -0
  119. package/dist/esm/src/packed/packed-bdd.d.ts +13 -0
  120. package/dist/esm/src/packed/packed-bdd.js +66 -0
  121. package/dist/esm/src/packed/packed-bdd.js.map +1 -0
  122. package/dist/esm/src/root-node.d.ts +1 -2
  123. package/dist/esm/src/root-node.js +5 -10
  124. package/dist/esm/src/root-node.js.map +1 -1
  125. package/dist/esm/src/types.d.ts +0 -12
  126. package/dist/esm/src/util.js +14 -5
  127. package/dist/esm/src/util.js.map +1 -1
  128. package/docs/variable-ordering.md +306 -0
  129. package/package.json +15 -12
  130. package/src/abstract-node.ts +7 -5
  131. package/src/branches.ts +9 -7
  132. package/src/create-bdd-from-truth-table.ts +1 -1
  133. package/src/index.ts +2 -1
  134. package/src/internal-node.ts +7 -4
  135. package/src/optimize-brute-force.ts +5 -7
  136. package/src/ordering/build-bdd.ts +70 -0
  137. package/src/ordering/dense-truth-table.ts +161 -0
  138. package/src/ordering/exact-cost-ordering.ts +425 -0
  139. package/src/ordering/exact-ordering.ts +317 -0
  140. package/src/ordering/index.ts +57 -0
  141. package/src/ordering/int-pair-map.ts +85 -0
  142. package/src/ordering/local-search.ts +369 -0
  143. package/src/ordering/optimize-ordering.ts +570 -0
  144. package/src/ordering/ordering-evaluator.ts +479 -0
  145. package/src/ordering/shared-diagram.ts +310 -0
  146. package/src/packed/index.ts +2 -0
  147. package/src/packed/packed-bdd-base64-string.ts +47 -0
  148. package/src/packed/packed-bdd.ts +98 -0
  149. package/src/root-node.ts +8 -17
  150. package/src/types.ts +0 -18
  151. package/src/util.ts +15 -6
  152. package/dist/cjs/src/minimal-string/bdd-to-minimal-string.d.ts +0 -8
  153. package/dist/cjs/src/minimal-string/bdd-to-minimal-string.js +0 -60
  154. package/dist/cjs/src/minimal-string/bdd-to-minimal-string.js.map +0 -1
  155. package/dist/cjs/src/minimal-string/bdd-to-simple-bdd.d.ts +0 -11
  156. package/dist/cjs/src/minimal-string/bdd-to-simple-bdd.js +0 -24
  157. package/dist/cjs/src/minimal-string/bdd-to-simple-bdd.js.map +0 -1
  158. package/dist/cjs/src/minimal-string/index.d.ts +0 -5
  159. package/dist/cjs/src/minimal-string/index.js.map +0 -1
  160. package/dist/cjs/src/minimal-string/minimal-string-to-simple-bdd.d.ts +0 -2
  161. package/dist/cjs/src/minimal-string/minimal-string-to-simple-bdd.js +0 -58
  162. package/dist/cjs/src/minimal-string/minimal-string-to-simple-bdd.js.map +0 -1
  163. package/dist/cjs/src/minimal-string/resolve-with-simple-bdd.d.ts +0 -2
  164. package/dist/cjs/src/minimal-string/resolve-with-simple-bdd.js +0 -21
  165. package/dist/cjs/src/minimal-string/resolve-with-simple-bdd.js.map +0 -1
  166. package/dist/cjs/src/minimal-string/string-format.d.ts +0 -9
  167. package/dist/cjs/src/minimal-string/string-format.js +0 -61
  168. package/dist/cjs/src/minimal-string/string-format.js.map +0 -1
  169. package/dist/esm/src/minimal-string/bdd-to-minimal-string.d.ts +0 -8
  170. package/dist/esm/src/minimal-string/bdd-to-minimal-string.js +0 -55
  171. package/dist/esm/src/minimal-string/bdd-to-minimal-string.js.map +0 -1
  172. package/dist/esm/src/minimal-string/bdd-to-simple-bdd.d.ts +0 -11
  173. package/dist/esm/src/minimal-string/bdd-to-simple-bdd.js +0 -19
  174. package/dist/esm/src/minimal-string/bdd-to-simple-bdd.js.map +0 -1
  175. package/dist/esm/src/minimal-string/index.d.ts +0 -5
  176. package/dist/esm/src/minimal-string/index.js +0 -6
  177. package/dist/esm/src/minimal-string/index.js.map +0 -1
  178. package/dist/esm/src/minimal-string/minimal-string-to-simple-bdd.d.ts +0 -2
  179. package/dist/esm/src/minimal-string/minimal-string-to-simple-bdd.js +0 -54
  180. package/dist/esm/src/minimal-string/minimal-string-to-simple-bdd.js.map +0 -1
  181. package/dist/esm/src/minimal-string/resolve-with-simple-bdd.d.ts +0 -2
  182. package/dist/esm/src/minimal-string/resolve-with-simple-bdd.js +0 -17
  183. package/dist/esm/src/minimal-string/resolve-with-simple-bdd.js.map +0 -1
  184. package/dist/esm/src/minimal-string/string-format.d.ts +0 -9
  185. package/dist/esm/src/minimal-string/string-format.js +0 -54
  186. package/dist/esm/src/minimal-string/string-format.js.map +0 -1
  187. package/src/minimal-string/bdd-to-minimal-string.ts +0 -72
  188. package/src/minimal-string/bdd-to-simple-bdd.ts +0 -31
  189. package/src/minimal-string/index.ts +0 -5
  190. package/src/minimal-string/minimal-string-to-simple-bdd.ts +0 -67
  191. package/src/minimal-string/resolve-with-simple-bdd.ts +0 -25
  192. package/src/minimal-string/string-format.ts +0 -67
@@ -0,0 +1,317 @@
1
+ import type { DenseTruthTable } from './dense-truth-table.js';
2
+ import { naturalOrdering } from './dense-truth-table.js';
3
+ import { OrderingEvaluator } from './ordering-evaluator.js';
4
+ import { SharedDiagram, StateHeap, DiagramBudgetExceededError, DEFAULT_MAX_DIAGRAM_NODES } from './shared-diagram.js';
5
+
6
+ export interface ExactOrderingOptions {
7
+ /**
8
+ * The search stops without a proof when more states were expanded.
9
+ * Default: 200000
10
+ */
11
+ maxExpansions?: number;
12
+ /**
13
+ * The search stops without a proof when the stored cofactor sets
14
+ * contain more entries in total (memory protection).
15
+ * Default: 2^25
16
+ */
17
+ maxCofactorEntries?: number;
18
+ /**
19
+ * A known good ordering. Its size is used as upper bound to prune the search,
20
+ * which makes the search much faster when the ordering is already close to optimal.
21
+ */
22
+ upperBoundOrdering?: ArrayLike<number>;
23
+ /**
24
+ * Only orderings with fewer internal nodes than this are searched for.
25
+ */
26
+ upperBound?: number;
27
+ log?: (message: string) => void;
28
+ }
29
+
30
+ export interface ExactOrderingResult {
31
+ /**
32
+ * true when it is proven that no ordering has fewer internal nodes
33
+ * than the returned one (or than the upper bound, when ordering is null)
34
+ */
35
+ solved: boolean;
36
+ /**
37
+ * The best ordering that was found. Irrelevant boolean functions
38
+ * are always at the bottom. null when no ordering below the numeric
39
+ * upper bound exists and the upperBoundOrdering (if given) is not
40
+ * below it either.
41
+ */
42
+ ordering: number[] | null;
43
+ /** amount of internal (non-leaf) nodes of `ordering`, -1 when ordering is null */
44
+ internalNodeCount: number;
45
+ /** amount of expanded states */
46
+ expansions: number;
47
+ /** amount of generated states */
48
+ generated: number;
49
+ }
50
+
51
+ /**
52
+ * The number of nodes on a level of a reduced BDD only depends on the SET of
53
+ * variables above that level, not on their order (Friedman & Supowit, 1990):
54
+ * it is the amount of distinct cofactors with respect to that set that still
55
+ * depend on the level's variable.
56
+ *
57
+ * Therefore the optimal ordering is a shortest path through the lattice of
58
+ * variable subsets, from the empty set to the full set, where the edge
59
+ * S -> S + {x} costs the amount of nodes that test x when exactly the
60
+ * variables of S are above it.
61
+ *
62
+ * This class finds that shortest path with an A* search. Each subset S is
63
+ * represented by the set of its distinct non-constant cofactors, which are
64
+ * nodes of one shared hash-consed diagram in the natural variable order
65
+ * (so equal functions always have equal ids). The admissible estimate for the
66
+ * remaining cost of a subset is the amount of its distinct non-constant
67
+ * cofactors: each of them needs its own node below.
68
+ *
69
+ * With a good upper bound (from the local search) the search typically only
70
+ * has to look at a tiny fraction of the 2^n subsets.
71
+ */
72
+ export class ExactOrderingSearch {
73
+ private readonly diagram: SharedDiagram;
74
+ private readonly n: number;
75
+ private readonly terminalCount: number;
76
+
77
+ constructor(
78
+ private readonly table: DenseTruthTable,
79
+ options: { maxDiagramNodes?: number } = {}
80
+ ) {
81
+ if (table.dontCareTerminal >= 0) {
82
+ throw new Error('the exact search needs a truth table without don\'t-care rows, resolve them with OrderingEvaluator.complete() first');
83
+ }
84
+ this.diagram = new SharedDiagram(
85
+ table,
86
+ options.maxDiagramNodes === undefined ? DEFAULT_MAX_DIAGRAM_NODES : options.maxDiagramNodes
87
+ );
88
+ this.n = table.variableCount;
89
+ this.terminalCount = table.terminalValues.length;
90
+ }
91
+
92
+ /**
93
+ * Moves the boolean functions the truth table does not depend on
94
+ * to the bottom. This never changes the amount of internal nodes
95
+ * but ensures the root node tests a relevant boolean function.
96
+ */
97
+ public normalizeOrdering(ordering: ArrayLike<number>): number[] {
98
+ return this.diagram.normalizeOrdering(ordering);
99
+ }
100
+
101
+ public run(options: ExactOrderingOptions = {}): ExactOrderingResult {
102
+ const log = options.log ? options.log : () => null;
103
+ const maxExpansions = options.maxExpansions === undefined ? 200000 : options.maxExpansions;
104
+ const maxCofactorEntries = options.maxCofactorEntries === undefined ? Math.pow(2, 25) : options.maxCofactorEntries;
105
+ const diagram = this.diagram;
106
+ const relevant = diagram.relevant;
107
+ const m = relevant.length;
108
+ const irrelevant = diagram.irrelevantVariables();
109
+
110
+ // upper bound
111
+ let upperBound = options.upperBound === undefined ? Infinity : options.upperBound;
112
+ let incumbent: number[] | null = null;
113
+ if (options.upperBoundOrdering) {
114
+ incumbent = this.normalizeOrdering(options.upperBoundOrdering);
115
+ const incumbentCount = new OrderingEvaluator(this.table).evaluate(incumbent).internalNodeCount;
116
+ if (incumbentCount >= upperBound) {
117
+ // the numeric bound is at least as good, the incumbent cannot be the answer
118
+ incumbent = null;
119
+ } else {
120
+ upperBound = incumbentCount;
121
+ }
122
+ }
123
+ const giveUp = (reason: string, expansionCount: number, generatedCount: number): ExactOrderingResult => {
124
+ log('exact search: ' + reason);
125
+ return {
126
+ solved: false,
127
+ ordering: incumbent,
128
+ internalNodeCount: incumbent ? upperBound : -1,
129
+ expansions: expansionCount,
130
+ generated: generatedCount
131
+ };
132
+ };
133
+ if (diagram.overBudget) {
134
+ return giveUp('diagram budget exceeded', 0, 0);
135
+ }
136
+
137
+ if (m === 0) {
138
+ // constant function: every ordering has 0 internal nodes
139
+ const constantOrdering = upperBound > 0 ? naturalOrdering(this.n) : incumbent;
140
+ return {
141
+ solved: true,
142
+ ordering: constantOrdering,
143
+ internalNodeCount: constantOrdering ? 0 : -1,
144
+ expansions: 0,
145
+ generated: 0
146
+ };
147
+ }
148
+
149
+ const full = (1 << m) - 1;
150
+ const cofactors: Map<number, Int32Array> = new Map();
151
+ const bestCost: Map<number, number> = new Map();
152
+ const parentVariable: Map<number, number> = new Map();
153
+ const closed: Set<number> = new Set();
154
+ const heap = new StateHeap();
155
+
156
+ const startSet = new Int32Array(1);
157
+ startSet[0] = diagram.rootId;
158
+ cofactors.set(0, startSet);
159
+ bestCost.set(0, 0);
160
+ heap.push(this.estimate(0, startSet), 0, 0);
161
+
162
+ let expansions = 0;
163
+ let generated = 1;
164
+ let storedEntries = 1;
165
+ let solvedSet = -1;
166
+ let solvedCost = -1;
167
+ try {
168
+ while (heap.size > 0) {
169
+ const state = heap.pop();
170
+ const currentCost = state.cost;
171
+ const subset = state.subset;
172
+ if (currentCost !== bestCost.get(subset) || closed.has(subset)) {
173
+ // stale heap entry
174
+ continue;
175
+ }
176
+ if (state.estimate >= upperBound) {
177
+ // nothing left that can beat the upper bound
178
+ break;
179
+ }
180
+ if (subset === full) {
181
+ solvedSet = subset;
182
+ solvedCost = currentCost;
183
+ break;
184
+ }
185
+ closed.add(subset);
186
+ expansions++;
187
+ if (expansions > maxExpansions) {
188
+ return giveUp('expansion budget exceeded', expansions, generated);
189
+ }
190
+ if (storedEntries > maxCofactorEntries) {
191
+ return giveUp('memory budget exceeded', expansions, generated);
192
+ }
193
+ if (expansions % 10000 === 0) {
194
+ log('exact search: ' + expansions + ' states expanded, ' + generated + ' generated, ' + heap.size + ' open');
195
+ }
196
+
197
+ const set = cofactors.get(subset) as Int32Array;
198
+ for (let c = 0; c < m; c++) {
199
+ const bit = 1 << c;
200
+ if ((subset & bit) !== 0) {
201
+ continue;
202
+ }
203
+ const variable = relevant[c];
204
+ let edgeCost = 0;
205
+ for (let i = 0; i < set.length; i++) {
206
+ if ((diagram.supportOf(set[i]) & bit) !== 0) {
207
+ edgeCost++;
208
+ }
209
+ }
210
+ const nextCost = currentCost + edgeCost;
211
+ const nextSubset = subset | bit;
212
+ const knownCost = bestCost.get(nextSubset);
213
+ if (knownCost !== undefined && knownCost <= nextCost) {
214
+ continue;
215
+ }
216
+ let nextSet = cofactors.get(nextSubset);
217
+ if (nextSet === undefined) {
218
+ nextSet = this.cofactorsAfter(set, variable);
219
+ cofactors.set(nextSubset, nextSet);
220
+ generated++;
221
+ storedEntries += nextSet.length;
222
+ }
223
+ const estimate = nextCost + this.estimate(nextSubset, nextSet);
224
+ if (estimate >= upperBound) {
225
+ continue;
226
+ }
227
+ bestCost.set(nextSubset, nextCost);
228
+ parentVariable.set(nextSubset, variable);
229
+ closed.delete(nextSubset);
230
+ heap.push(estimate, nextCost, nextSubset);
231
+ }
232
+ }
233
+ } catch (err) {
234
+ if (err instanceof DiagramBudgetExceededError) {
235
+ return giveUp('diagram budget exceeded', expansions, generated);
236
+ }
237
+ throw err;
238
+ }
239
+
240
+ if (solvedSet === -1) {
241
+ // no ordering below the upper bound exists: the incumbent (if any) is optimal
242
+ return {
243
+ solved: true,
244
+ ordering: incumbent,
245
+ internalNodeCount: incumbent ? upperBound : -1,
246
+ expansions,
247
+ generated
248
+ };
249
+ }
250
+
251
+ // reconstruct the path
252
+ const reversed: number[] = [];
253
+ let remainingSubset = solvedSet;
254
+ while (remainingSubset !== 0) {
255
+ const variable = parentVariable.get(remainingSubset) as number;
256
+ reversed.push(variable);
257
+ remainingSubset = remainingSubset & ~(1 << diagram.compactOf[variable]);
258
+ }
259
+ const ordering = reversed.reverse().concat(irrelevant);
260
+ return {
261
+ solved: true,
262
+ ordering,
263
+ internalNodeCount: solvedCost,
264
+ expansions,
265
+ generated
266
+ };
267
+ }
268
+
269
+ /**
270
+ * lower bound for the amount of nodes below the given subset
271
+ */
272
+ private estimate(subset: number, set: Int32Array): number {
273
+ let union = 0;
274
+ for (let i = 0; i < set.length; i++) {
275
+ union |= this.diagram.supportOf(set[i]);
276
+ }
277
+ const remaining = union & ~subset;
278
+ let remainingCount = 0;
279
+ for (let x = remaining; x !== 0; x &= x - 1) {
280
+ remainingCount++;
281
+ }
282
+ return Math.max(set.length, remainingCount);
283
+ }
284
+
285
+ /**
286
+ * the distinct non-constant cofactors after additionally fixing `variable`
287
+ */
288
+ private cofactorsAfter(set: Int32Array, variable: number): Int32Array {
289
+ const diagram = this.diagram;
290
+ const bit = 1 << diagram.compactOf[variable];
291
+ const candidates = new Int32Array(set.length * 2);
292
+ let count = 0;
293
+ for (let i = 0; i < set.length; i++) {
294
+ const g = set[i];
295
+ if ((diagram.supportOf(g) & bit) === 0) {
296
+ candidates[count++] = g;
297
+ continue;
298
+ }
299
+ const lo = diagram.restrict(g, variable, 0);
300
+ if (lo >= this.terminalCount) {
301
+ candidates[count++] = lo;
302
+ }
303
+ const hi = diagram.restrict(g, variable, 1);
304
+ if (hi >= this.terminalCount) {
305
+ candidates[count++] = hi;
306
+ }
307
+ }
308
+ const sorted = candidates.subarray(0, count).sort();
309
+ let unique = 0;
310
+ for (let i = 0; i < sorted.length; i++) {
311
+ if (i === 0 || sorted[i] !== sorted[i - 1]) {
312
+ sorted[unique++] = sorted[i];
313
+ }
314
+ }
315
+ return sorted.slice(0, unique);
316
+ }
317
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Public API of the variable ordering optimizer, see docs/variable-ordering.md.
3
+ *
4
+ * - optimize-ordering.ts: optimizeOrdering(), findOrdering(), createBddWithOrdering()
5
+ * - ordering-evaluator.ts: counts the nodes (and the expected cost) of any ordering in O(2^n)
6
+ * - local-search.ts: deterministic seeds, sifting and window permutation
7
+ * - exact-ordering.ts, exact-cost-ordering.ts: A* searches that prove optimality
8
+ * - shared-diagram.ts: the shared function diagram the exact searches work on
9
+ * - dense-truth-table.ts, build-bdd.ts, int-pair-map.ts: data structures and helpers
10
+ */
11
+ export {
12
+ optimizeOrdering,
13
+ findOrdering,
14
+ createBddWithOrdering,
15
+ buildCostModel,
16
+ DEFAULT_MAX_EXACT_VARIABLES,
17
+ DEFAULT_MAX_EXACT_NODE_COUNT
18
+ } from './optimize-ordering.js';
19
+ export type {
20
+ FindOrderingInput,
21
+ FindOrderingResult,
22
+ OptimizeOrderingInput,
23
+ OptimizeOrderingResult,
24
+ OptimizeOrderingStats
25
+ } from './optimize-ordering.js';
26
+ export {
27
+ OrderingEvaluator,
28
+ OrderingCursor
29
+ } from './ordering-evaluator.js';
30
+ export type {
31
+ CostModel,
32
+ OrderingEvaluation,
33
+ ReducedDiagram
34
+ } from './ordering-evaluator.js';
35
+ export {
36
+ LocalOrderingSearch,
37
+ DEFAULT_WINDOW_SIZE,
38
+ DEFAULT_MAX_ROUNDS,
39
+ MAX_WINDOW_SIZE
40
+ } from './local-search.js';
41
+ export type {
42
+ LocalSearchOptions,
43
+ OrderingSearchResult,
44
+ OrderingProgress,
45
+ OrderingProgressCallback
46
+ } from './local-search.js';
47
+ export { ExactOrderingSearch } from './exact-ordering.js';
48
+ export type { ExactOrderingOptions, ExactOrderingResult } from './exact-ordering.js';
49
+ export { ExactCostOrderingSearch } from './exact-cost-ordering.js';
50
+ export type { ExactCostOrderingOptions, ExactCostOrderingResult } from './exact-cost-ordering.js';
51
+ export {
52
+ parseTruthTable,
53
+ denseTruthTableToMap,
54
+ naturalOrdering,
55
+ MAX_VARIABLE_COUNT
56
+ } from './dense-truth-table.js';
57
+ export type { DenseTruthTable } from './dense-truth-table.js';
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Open addressing hash map from a pair of non-negative 32 bit integers
3
+ * to a non-negative 32 bit integer. Used in the hot paths of the exact search
4
+ * where a Map with string keys would be far too slow.
5
+ */
6
+ export class IntPairMap {
7
+ public size: number = 0;
8
+ private keysA: Int32Array;
9
+ private keysB: Int32Array;
10
+ private values: Int32Array;
11
+ private mask: number;
12
+
13
+ constructor(initialCapacity: number = 1024) {
14
+ let capacity = 16;
15
+ while (capacity < initialCapacity) {
16
+ capacity *= 2;
17
+ }
18
+ this.keysA = new Int32Array(capacity).fill(-1);
19
+ this.keysB = new Int32Array(capacity);
20
+ this.values = new Int32Array(capacity);
21
+ this.mask = capacity - 1;
22
+ }
23
+
24
+ /**
25
+ * @returns the stored value or -1
26
+ */
27
+ public get(a: number, b: number): number {
28
+ let h = this.hash(a, b);
29
+ while (true) {
30
+ const storedA = this.keysA[h];
31
+ if (storedA === -1) {
32
+ return -1;
33
+ }
34
+ if (storedA === a && this.keysB[h] === b) {
35
+ return this.values[h];
36
+ }
37
+ h = (h + 1) & this.mask;
38
+ }
39
+ }
40
+
41
+ public set(a: number, b: number, value: number) {
42
+ if ((this.size + 1) * 2 > this.keysA.length) {
43
+ this.grow();
44
+ }
45
+ let h = this.hash(a, b);
46
+ while (true) {
47
+ const storedA = this.keysA[h];
48
+ if (storedA === -1) {
49
+ this.keysA[h] = a;
50
+ this.keysB[h] = b;
51
+ this.values[h] = value;
52
+ this.size++;
53
+ return;
54
+ }
55
+ if (storedA === a && this.keysB[h] === b) {
56
+ this.values[h] = value;
57
+ return;
58
+ }
59
+ h = (h + 1) & this.mask;
60
+ }
61
+ }
62
+
63
+ private hash(a: number, b: number): number {
64
+ let h = (Math.imul(a, 0x9E3779B1) ^ Math.imul(b + 0x7F4A7C15, 0x85EBCA77)) >>> 0;
65
+ h ^= h >>> 15;
66
+ return h & this.mask;
67
+ }
68
+
69
+ private grow() {
70
+ const oldA = this.keysA;
71
+ const oldB = this.keysB;
72
+ const oldValues = this.values;
73
+ const capacity = oldA.length * 2;
74
+ this.keysA = new Int32Array(capacity).fill(-1);
75
+ this.keysB = new Int32Array(capacity);
76
+ this.values = new Int32Array(capacity);
77
+ this.mask = capacity - 1;
78
+ this.size = 0;
79
+ for (let i = 0; i < oldA.length; i++) {
80
+ if (oldA[i] !== -1) {
81
+ this.set(oldA[i], oldB[i], oldValues[i]);
82
+ }
83
+ }
84
+ }
85
+ }