compiled-knowledge 4.0.0a12__cp312-cp312-win_amd64.whl → 4.0.0a16__cp312-cp312-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.
Potentially problematic release.
This version of compiled-knowledge might be problematic. Click here for more details.
- ck/circuit/circuit.cp312-win_amd64.pyd +0 -0
- ck/circuit_compiler/cython_vm_compiler/_compiler.cp312-win_amd64.pyd +0 -0
- ck/circuit_compiler/named_circuit_compilers.py +8 -8
- ck/example/pathfinder.py +120 -47
- ck/in_out/parse_ace_lmap.py +1 -1
- ck/in_out/parse_ace_nnf.py +72 -38
- ck/in_out/parser_utils.py +1 -1
- ck/pgm_compiler/ace/ace.py +11 -8
- ck/pgm_compiler/factor_elimination.py +3 -3
- ck/pgm_compiler/named_pgm_compilers.py +26 -26
- ck/pgm_compiler/recursive_conditioning.py +1 -1
- ck/pgm_compiler/support/circuit_table/circuit_table.cp312-win_amd64.pyd +0 -0
- ck/pgm_compiler/support/factor_tables.py +34 -26
- ck/pgm_compiler/variable_elimination.py +1 -1
- ck/utils/local_config.py +270 -0
- ck_demos/ace/demo_ace.py +1 -1
- ck_demos/pgm_compiler/compare_pgm_compilers.py +15 -2
- ck_demos/utils/compare.py +50 -22
- {compiled_knowledge-4.0.0a12.dist-info → compiled_knowledge-4.0.0a16.dist-info}/METADATA +2 -2
- {compiled_knowledge-4.0.0a12.dist-info → compiled_knowledge-4.0.0a16.dist-info}/RECORD +23 -22
- {compiled_knowledge-4.0.0a12.dist-info → compiled_knowledge-4.0.0a16.dist-info}/WHEEL +0 -0
- {compiled_knowledge-4.0.0a12.dist-info → compiled_knowledge-4.0.0a16.dist-info}/licenses/LICENSE.txt +0 -0
- {compiled_knowledge-4.0.0a12.dist-info → compiled_knowledge-4.0.0a16.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -18,19 +18,19 @@ class NamedCircuitCompiler(Enum):
|
|
|
18
18
|
Each member itself is callable, conforming to the CircuitCompiler protocol, delegating to the compiler function.
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
LLVM_STACK
|
|
22
|
-
LLVM_TMPS
|
|
23
|
-
LLVM_VM
|
|
24
|
-
CYTHON_VM
|
|
25
|
-
INTERPRET
|
|
21
|
+
LLVM_STACK = (partial(llvm_compiler.compile_circuit, flavour=Flavour.STACK),)
|
|
22
|
+
LLVM_TMPS = (partial(llvm_compiler.compile_circuit, flavour=Flavour.TMPS, opt=0),)
|
|
23
|
+
LLVM_VM = (llvm_vm_compiler.compile_circuit,)
|
|
24
|
+
CYTHON_VM = (cython_vm_compiler.compile_circuit,)
|
|
25
|
+
INTERPRET = (interpret_compiler.compile_circuit,)
|
|
26
26
|
|
|
27
|
-
# The following circuit compilers were experimental but are not really useful
|
|
27
|
+
# The following circuit compilers were experimental but are not really useful.
|
|
28
28
|
#
|
|
29
29
|
# Slow compile and execution:
|
|
30
|
-
# LLVM_FUNCS
|
|
30
|
+
# LLVM_FUNCS = (partial(llvm_compiler.compile_circuit, flavour=Flavour.FUNCS, opt=0),)
|
|
31
31
|
#
|
|
32
32
|
# Slow compile and same execution as LLVM_VM:
|
|
33
|
-
# LLVM_VM_COMPILED_ARRAYS
|
|
33
|
+
# LLVM_VM_COMPILED_ARRAYS = (partial(llvm_vm_compiler.compile_circuit, compile_arrays=True),)
|
|
34
34
|
|
|
35
35
|
def __call__(
|
|
36
36
|
self,
|
ck/example/pathfinder.py
CHANGED
|
@@ -2,25 +2,33 @@ from ck.pgm import PGM
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class Pathfinder(PGM):
|
|
5
|
+
"""
|
|
6
|
+
This implementation of the Pathfinder PGM has 93 states for the "Fault" random
|
|
7
|
+
variable, and its factor has been normalised to be a CPT.
|
|
8
|
+
"""
|
|
5
9
|
|
|
6
10
|
def __init__(self):
|
|
7
11
|
super().__init__(self.__class__.__name__)
|
|
8
12
|
|
|
9
13
|
pgm_rv0 = self.new_rv('Fault', (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
'AIDS early', 'AILD', 'ALIP', 'Cat scratch disease', 'Dermatopathic laden', 'Florid follic hyperp',
|
|
15
|
+
'GLH hyaline vascular', 'GLH plasma cell', 'Granulomatous laden', 'Histiocytosis x', 'Infectious mono',
|
|
16
|
+
'Leprosy-lepromatous', 'Lymphangiographic', 'Mantle zone hyperpla', 'Necrotizing Kikuchi',
|
|
17
|
+
'Necrotiz non-Kikuchi', 'Rheumatoid arthritis', 'Sarcoidosis', 'SHML', 'Sinus histiocytosis', 'Syphilis',
|
|
18
|
+
'Toxoplasmosis', 'Tuberculosis', 'Viral NOS', "Whipple's disease", 'L&H nodular HD', 'L&H diffuse HD',
|
|
19
|
+
'Nodular sclerosis HD', 'Cellular phase NSHD', 'Syncytial NSHD', 'Mixed cellularity HD',
|
|
20
|
+
'Interfollicular HD',
|
|
21
|
+
'Diffuse fibrosis HD', 'Reticular-type HD', 'Small cleaved fol', 'Mixed fol', 'Large cell fol',
|
|
22
|
+
'Small noncleaved fol', 'Small lymphocytic', 'Plasmacytoid lyctic', 'Mantle zone lymphoma',
|
|
23
|
+
'Small cleaved dif',
|
|
24
|
+
'Mixed FCC dif', 'Large cell dif', 'B-immunoblastic', 'T-immunob mixed', 'AILD-like T-cell lym',
|
|
25
|
+
'Japanese ATL',
|
|
26
|
+
'Lymphoblastic', 'Small noncleaved dif', 'True histiocytic', 'Ki-1 LC anaplas T', 'Multiple myeloma',
|
|
27
|
+
'Mycosis fungoides', 'AML', 'Hairy cell leukemia', 'Carcinoma', 'Melanoma', 'EM plasmacytoma',
|
|
28
|
+
"Kaposi's sarcoma", 'Mast-cell disease', 'AIDS involutionary', 'T-immunob large', 'Monocytoid B-cell',
|
|
29
|
+
'Nasopharyngeal CA', 'Seminoma', 'SLE', 'Mycobact histiocytos', 'Mast-cell hyperplas', 'LGV',
|
|
30
|
+
'Histoplasmosis',
|
|
31
|
+
'Coccidioidomycosis', 'CMV', 'Brucellosis', 'Ki-1 LC anaplas B', 'Intermed lymphocytic'))
|
|
24
32
|
pgm_rv1 = self.new_rv('ACID', ('Negative', 'Positive'))
|
|
25
33
|
pgm_rv2 = self.new_rv('F_CSA', ('No follicles', 'Absent', 'Present'))
|
|
26
34
|
pgm_rv3 = self.new_rv('F_ISL', ('No follicles', 'Absent', 'Present'))
|
|
@@ -58,25 +66,27 @@ class Pathfinder(PGM):
|
|
|
58
66
|
('Absent [0]', 'Rare [1-5]', 'Few [6-25]', 'Many [26-100]', 'Striking [>100]'))
|
|
59
67
|
pgm_rv34 = self.new_rv('LH_NOD', ('Absent', 'Present'))
|
|
60
68
|
pgm_rv35 = self.new_rv('LACUN', (
|
|
61
|
-
|
|
69
|
+
'Absent [0]', 'Rare [1-5]', 'Few [6-25]', 'Many [26 - 100]', 'Striking [>100]',
|
|
70
|
+
'Sheets [>50pc of section]'))
|
|
62
71
|
pgm_rv36 = self.new_rv('LANG', ('Absent [0]', 'Present [1-5]', 'Prominent [>5]'))
|
|
63
72
|
pgm_rv37 = self.new_rv('LEUK', ('Absent', 'Present'))
|
|
64
73
|
pgm_rv38 = self.new_rv('LLC_CY', ('No LLCs', 'Scanty', 'Moderate-abundant clear', 'Moderate-abundant deep'))
|
|
65
74
|
pgm_rv39 = self.new_rv('LLC_NS', (
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
'No LLCs', 'Multilobated [some]', 'Cerebriform/Mulberry [some]', 'Convoluted [some]', 'Irregular [most]',
|
|
76
|
+
'Round/slightly irregular [most]'))
|
|
68
77
|
pgm_rv40 = self.new_rv('LLC', (
|
|
69
|
-
|
|
78
|
+
'Absent [0pc]', 'Sparse [<10pc]', 'Moderate [11-50pc]', 'Numerous [51-90pc]', 'Striking [>90pc]'))
|
|
70
79
|
pgm_rv41 = self.new_rv('F_POPU', (
|
|
71
|
-
|
|
80
|
+
'No follicles', '>80pc 6-12u', '>50pc 13-20u with nucleoli', '>50pc >20u with nucleoli', 'Other'))
|
|
72
81
|
pgm_rv42 = self.new_rv('MAST', ('Absent [0]', 'Present [1-50]', 'Prominent [>50]'))
|
|
73
82
|
pgm_rv43 = self.new_rv('MLC', (
|
|
74
|
-
|
|
83
|
+
'Absent [0pc]', 'Sparse [<10pc]', 'Moderate [11-50pc]', 'Numerous [51-90pc]', 'Striking [>90pc]'))
|
|
75
84
|
pgm_rv44 = self.new_rv('MELANOMA', ('Absent', 'Present'))
|
|
76
85
|
pgm_rv45 = self.new_rv('MF10HP', ('0-5', '6-15', '16-50', '>50'))
|
|
77
86
|
pgm_rv46 = self.new_rv('MLC_CY', ('No MLCs', 'Scanty', 'Moderate-abundant clear', 'Moderate-abundant deep'))
|
|
78
87
|
pgm_rv47 = self.new_rv('MLC_NS', (
|
|
79
|
-
|
|
88
|
+
'No MLCs', 'Cerebriform [some]', 'Convoluted [some]', 'Irregular [most]',
|
|
89
|
+
'Round/slightly irregular [most]'))
|
|
80
90
|
pgm_rv48 = self.new_rv('MLC_NI', ('No MLCs', 'Absent to rare', 'Large central', 'Peripheral', 'Other'))
|
|
81
91
|
pgm_rv49 = self.new_rv('MCC', ('Absent [0pc]', 'Present [<5pc]', 'Prominent [5-50pc]', 'Confluence [>50pc]'))
|
|
82
92
|
pgm_rv50 = self.new_rv('MONO', ('Absent [0]', 'Rare [1-2]', 'Present [3-20]', 'Many [>20]'))
|
|
@@ -90,7 +100,8 @@ class Pathfinder(PGM):
|
|
|
90
100
|
pgm_rv57 = self.new_rv('PAS', ('No', 'Yes'))
|
|
91
101
|
pgm_rv58 = self.new_rv('PI', ('Absent', 'Present', 'Prominent'))
|
|
92
102
|
pgm_rv59 = self.new_rv('PLASMA', (
|
|
93
|
-
|
|
103
|
+
'Absent [0pc]', 'Few [<5pc]', 'Moderate [6-20pc]', 'Marked [21-50pc]', 'Striking [51-90pc]',
|
|
104
|
+
'Sheets [>90pc]'))
|
|
94
105
|
pgm_rv60 = self.new_rv('PC_TP', ('No plasma cells', 'Mature', 'Immature', 'Blastic', 'Pleomorphic', 'Mixed'))
|
|
95
106
|
pgm_rv61 = self.new_rv('PLEO', ('Absent [0]', 'Rare [1-5]', 'Few [6-25]', 'Many [26-100]', 'Striking [>100]'))
|
|
96
107
|
pgm_rv62 = self.new_rv('PFP', ('Absent', 'Present'))
|
|
@@ -101,10 +112,10 @@ class Pathfinder(PGM):
|
|
|
101
112
|
pgm_rv67 = self.new_rv('S_RING', ('Absent', 'Present'))
|
|
102
113
|
pgm_rv68 = self.new_rv('SLC_CY', ('No SLCs', 'Scanty', 'Moderate-abundant clear', 'Moderate-abundant deep'))
|
|
103
114
|
pgm_rv69 = self.new_rv('SLC_NS', (
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
'No SLCs', 'Cerebriform [some]', 'Convoluted [some]', 'Moderate-mark irregular [most]',
|
|
116
|
+
'Round/slightly irregular [most]'))
|
|
106
117
|
pgm_rv70 = self.new_rv('SLC', (
|
|
107
|
-
|
|
118
|
+
'Absent [0pc]', 'Sparse [1-10pc]', 'Moderate [11-50pc]', 'Numerous [51-90pc]', 'Striking [>90pc]'))
|
|
108
119
|
pgm_rv71 = self.new_rv('SINUSES', ('Distended', 'Patent', 'Part-greatly obliterated', 'Completely obliterated'))
|
|
109
120
|
pgm_rv72 = self.new_rv('VASC_C', ('Absent', 'Present'))
|
|
110
121
|
pgm_rv73 = self.new_rv('VASC_NS', ('Absent', 'Slight', 'Moderate', 'Marked', 'Pronounced'))
|
|
@@ -123,11 +134,12 @@ class Pathfinder(PGM):
|
|
|
123
134
|
pgm_rv86 = self.new_rv('F_SS',
|
|
124
135
|
('No follicles', 'Absent [0]', 'Slight [1-15]', 'Moderate [16-30]', 'Marked [>30]'))
|
|
125
136
|
pgm_rv87 = self.new_rv('F_MZSTAT', (
|
|
126
|
-
|
|
127
|
-
|
|
137
|
+
'No follicles/no mantle zones', 'Mantle zones absent in most follicles',
|
|
138
|
+
'Most incompletely surround follicles',
|
|
139
|
+
'Most completely surround fol not thick', 'Most thick [>10 lymphocytes]'))
|
|
128
140
|
pgm_rv88 = self.new_rv('F_MZCM', ('No follicles/no mantle zones', 'Absent', 'Present'))
|
|
129
141
|
pgm_rv89 = self.new_rv('F_DEF', (
|
|
130
|
-
|
|
142
|
+
'No follicles', 'Almost all well defined', 'Both well and poor defined', 'Almost all poor defined'))
|
|
131
143
|
pgm_rv90 = self.new_rv('F_MITO', ('No follicles', '0-20', '21-50', '>50'))
|
|
132
144
|
pgm_rv91 = self.new_rv('F_MZ', ('No follicles', 'Absent', 'Present'))
|
|
133
145
|
pgm_rv92 = self.new_rv('F_CIO', ('No follicles', 'Most similar', 'Some similar', 'None similar'))
|
|
@@ -169,13 +181,14 @@ class Pathfinder(PGM):
|
|
|
169
181
|
pgm_rv128 = self.new_rv('TB', ('Absent', 'Present', 'Prominent'))
|
|
170
182
|
pgm_rv129 = self.new_rv('MELANIN', ('Absent to normal', 'Present', 'Prominent'))
|
|
171
183
|
pgm_rv130 = self.new_rv('MC_SL_NS', (
|
|
172
|
-
|
|
184
|
+
'No follicles/no mantle zones', 'Round', 'Slightly irregular', 'Moderately irregular',
|
|
185
|
+
'Markedly irregular'))
|
|
173
186
|
pgm_rv131 = self.new_rv('FMZ_FUS', (
|
|
174
|
-
|
|
187
|
+
'No thick mantle zones', 'No nodules no fusion', 'Nodules but no fusion', 'Fusion with or without nodules'))
|
|
175
188
|
pgm_rv132 = self.new_rv('BH_NOS_L', ('No NOS benign histiocytes', 'Mainly interfollicular', 'Mainly sinusoidal',
|
|
176
189
|
'Both interfollicular and sinusoidal'))
|
|
177
190
|
pgm_rv133 = self.new_rv('SLC_NI', (
|
|
178
|
-
|
|
191
|
+
'No SLCs', 'None identifiable', '1 or more peripheral small', '1 central medium to large', 'Other'))
|
|
179
192
|
pgm_rv134 = self.new_rv('HYALIN', ('Absent', 'Present', 'Prominent'))
|
|
180
193
|
pgm_factor0 = self.new_factor(pgm_rv0)
|
|
181
194
|
pgm_factor1 = self.new_factor(pgm_rv1, pgm_rv128)
|
|
@@ -315,22 +328,82 @@ class Pathfinder(PGM):
|
|
|
315
328
|
|
|
316
329
|
pgm_function0 = pgm_factor0.set_dense()
|
|
317
330
|
pgm_function0.set_flat(
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
0.
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
331
|
+
0.018279029,
|
|
332
|
+
0.001370927,
|
|
333
|
+
0.004569757,
|
|
334
|
+
0.004569757,
|
|
335
|
+
0.022848787,
|
|
336
|
+
0.022848787,
|
|
337
|
+
0.004569757,
|
|
338
|
+
0.004569757,
|
|
339
|
+
0.013709272,
|
|
340
|
+
0.003655806,
|
|
341
|
+
0.001827903,
|
|
342
|
+
0.001827903,
|
|
343
|
+
0.004569757,
|
|
344
|
+
0.011424393,
|
|
345
|
+
0.007311612,
|
|
346
|
+
0.011698579,
|
|
347
|
+
0.001827903,
|
|
348
|
+
0.001827903,
|
|
349
|
+
0.002741854,
|
|
350
|
+
0.018279029,
|
|
351
|
+
0.001827903,
|
|
352
|
+
0.004569757,
|
|
353
|
+
0.003655806,
|
|
354
|
+
0.013709272,
|
|
355
|
+
0.000456976,
|
|
356
|
+
0.013709272,
|
|
357
|
+
0.002284879,
|
|
358
|
+
0.082255632,
|
|
359
|
+
0.007311612,
|
|
360
|
+
0.007311612,
|
|
361
|
+
0.063976603,
|
|
362
|
+
0.004569757,
|
|
363
|
+
0.002741854,
|
|
364
|
+
0.002741854,
|
|
365
|
+
0.100534662,
|
|
366
|
+
0.050267331,
|
|
367
|
+
0.018279029,
|
|
368
|
+
0.004569757,
|
|
369
|
+
0.036558059,
|
|
370
|
+
0.007311612,
|
|
371
|
+
0.009139515,
|
|
372
|
+
0.004569757,
|
|
373
|
+
0.005483709,
|
|
374
|
+
0.100534662,
|
|
375
|
+
0.018279029,
|
|
376
|
+
0.018279029,
|
|
377
|
+
0.010967418,
|
|
378
|
+
0.001827903,
|
|
379
|
+
0.009139515,
|
|
380
|
+
0.073116118,
|
|
381
|
+
0.000913951,
|
|
382
|
+
0.013709272,
|
|
383
|
+
0.001827903,
|
|
384
|
+
0.002741854,
|
|
385
|
+
0.004569757,
|
|
386
|
+
9.13951E-05,
|
|
387
|
+
0.018279029,
|
|
388
|
+
0.007311612,
|
|
389
|
+
0.004569757,
|
|
390
|
+
0.007311612,
|
|
391
|
+
0.001827903,
|
|
392
|
+
0.018279029,
|
|
393
|
+
0.022848787,
|
|
394
|
+
0.018279029,
|
|
395
|
+
0.004569757,
|
|
396
|
+
0.001827903,
|
|
397
|
+
0.001827903,
|
|
398
|
+
0.001827903,
|
|
399
|
+
0.001827903,
|
|
400
|
+
0.001827903,
|
|
401
|
+
0.001827903,
|
|
402
|
+
0.001827903,
|
|
403
|
+
0.004569757,
|
|
404
|
+
0.000228488,
|
|
405
|
+
0.003655806,
|
|
406
|
+
0.007311612,
|
|
334
407
|
)
|
|
335
408
|
|
|
336
409
|
pgm_function1 = pgm_factor1.set_dense()
|
ck/in_out/parse_ace_lmap.py
CHANGED
ck/in_out/parse_ace_nnf.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import Tuple, Optional, Dict, List
|
|
2
|
+
from typing import Tuple, Optional, Dict, List, Sequence
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
6
6
|
from ck.circuit import Circuit, CircuitNode, VarNode, ConstValue, ConstNode
|
|
7
7
|
from ck.in_out.parse_ace_lmap import LiteralMap
|
|
8
8
|
from ck.in_out.parser_utils import ParseError, ParserInput
|
|
9
|
+
from ck.pgm import Indicator
|
|
9
10
|
from ck.pgm_circuit.slot_map import SlotKey, SlotMap
|
|
10
11
|
from ck.utils.np_extras import NDArrayFloat64
|
|
11
12
|
|
|
@@ -18,9 +19,9 @@ def read_nnf_with_literal_map(
|
|
|
18
19
|
input_stream,
|
|
19
20
|
literal_map: LiteralMap,
|
|
20
21
|
*,
|
|
22
|
+
indicators: Sequence[Indicator] = (),
|
|
21
23
|
const_parameters: bool = True,
|
|
22
24
|
optimise_ops: bool = True,
|
|
23
|
-
circuit: Optional[Circuit] = None,
|
|
24
25
|
check_header: bool = False,
|
|
25
26
|
) -> Tuple[CircuitNode, SlotMap, NDArrayFloat64]:
|
|
26
27
|
"""
|
|
@@ -31,8 +32,8 @@ def read_nnf_with_literal_map(
|
|
|
31
32
|
|
|
32
33
|
Args:
|
|
33
34
|
input_stream: to parse, as per `ParserInput` argument.
|
|
35
|
+
indicators: any indicators to pre allocate to circuit variables.
|
|
34
36
|
literal_map: mapping from literal code to indicators.
|
|
35
|
-
circuit: an optional circuit to reuse.
|
|
36
37
|
check_header: if true, an exception is raised if the number of nodes or arcs is not as expected.
|
|
37
38
|
const_parameters: if true, the potential function parameters will be circuit
|
|
38
39
|
constants, otherwise they will be circuit variables.
|
|
@@ -44,6 +45,7 @@ def read_nnf_with_literal_map(
|
|
|
44
45
|
slot_map: is a map from indicator to a circuit var index (int).
|
|
45
46
|
params: is a numpy array of parameter values, co-indexed with `circuit.vars[num_indicators:]`
|
|
46
47
|
"""
|
|
48
|
+
circuit = Circuit()
|
|
47
49
|
|
|
48
50
|
# Set the `const_literals` parameter for `read_nnf`
|
|
49
51
|
const_literals: Optional[Dict[int, ConstValue]]
|
|
@@ -57,28 +59,42 @@ def read_nnf_with_literal_map(
|
|
|
57
59
|
else:
|
|
58
60
|
const_literals = {}
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
# Make a slot map to map from an indicator to a circuit variable index.
|
|
63
|
+
# Preload `var_literals` to map literal codes to circuit vars.
|
|
64
|
+
# We allocate the circuit variables here to ensure that indicators
|
|
65
|
+
# come before parameters in the circuit variables.
|
|
66
|
+
slot_map: Dict[SlotKey, int] = {
|
|
67
|
+
indicator: i
|
|
68
|
+
for i, indicator in enumerate(indicators)
|
|
69
|
+
}
|
|
70
|
+
circuit.new_vars(len(slot_map))
|
|
71
|
+
var_literals: Dict[int, int] = {}
|
|
72
|
+
for literal_code, indicator in literal_map.indicators.items():
|
|
73
|
+
slot = slot_map.get(indicator)
|
|
74
|
+
if slot is None:
|
|
75
|
+
slot = circuit.new_var().idx
|
|
76
|
+
slot_map[indicator] = slot
|
|
77
|
+
var_literals[literal_code] = slot
|
|
78
|
+
num_indicators: int = len(slot_map)
|
|
79
|
+
|
|
80
|
+
# Parse the nnf file
|
|
81
|
+
top_node, vars_literals = read_nnf(
|
|
61
82
|
input_stream,
|
|
83
|
+
var_literals=var_literals,
|
|
62
84
|
const_literals=const_literals,
|
|
63
85
|
circuit=circuit,
|
|
64
86
|
check_header=check_header,
|
|
65
87
|
optimise_ops=optimise_ops,
|
|
66
88
|
)
|
|
67
89
|
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
for literal_code, indicator in literal_map.indicators.items()
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
# Get the parameter values
|
|
75
|
-
num_indicators: int = len(literal_map.indicators)
|
|
90
|
+
# Get the parameter values.
|
|
91
|
+
# Any new circuit vars added to the circuit are parameters.
|
|
92
|
+
# Parameter IDs are not added to the slot map as we don't know them.
|
|
76
93
|
num_parameters: int = top_node.circuit.number_of_vars - num_indicators
|
|
77
94
|
assert num_parameters == 0 or not const_parameters, 'const_parameters -> num_parameters == 0'
|
|
78
|
-
|
|
79
95
|
params: NDArrayFloat64 = np.zeros(num_parameters, dtype=np.float64)
|
|
80
96
|
for literal_code, value in literal_map.params.items():
|
|
81
|
-
literal_slot: Optional[int] =
|
|
97
|
+
literal_slot: Optional[int] = var_literals.get(literal_code)
|
|
82
98
|
if literal_slot is not None and literal_slot >= num_indicators:
|
|
83
99
|
params[literal_slot - num_indicators] = value
|
|
84
100
|
|
|
@@ -88,6 +104,7 @@ def read_nnf_with_literal_map(
|
|
|
88
104
|
def read_nnf(
|
|
89
105
|
input_stream,
|
|
90
106
|
*,
|
|
107
|
+
var_literals: Optional[Dict[int, int]] = None,
|
|
91
108
|
const_literals: Optional[Dict[int, ConstValue]] = None,
|
|
92
109
|
circuit: Optional[Circuit] = None,
|
|
93
110
|
check_header: bool = False,
|
|
@@ -99,44 +116,51 @@ def read_nnf(
|
|
|
99
116
|
The input consists of propositional logical sentences in negative normal form (NNF).
|
|
100
117
|
This covers both ".ac" and ".nnf" files produced by the software ACE.
|
|
101
118
|
|
|
102
|
-
|
|
103
|
-
|
|
119
|
+
This function returns the last node parsed (or the constant zero node if no nodes passed).
|
|
120
|
+
It also returns a mapping from literal code (int) to circuit variable index.
|
|
104
121
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
122
|
+
Two optional dictionaries may be supplied. Dictionary `var_literals` maps a literal
|
|
123
|
+
code to a pre-existing circuit variable index. Dictionary `const_literals` maps a literal
|
|
124
|
+
code to a constant value. A literal code should not appear in both dictionaries.
|
|
125
|
+
|
|
126
|
+
Any literal code that is parsed but does not appear in `var_literals` or `const_literals`
|
|
127
|
+
results in a new circuit variable being created and a corresponding entry added to
|
|
128
|
+
`var_literals`.
|
|
129
|
+
|
|
130
|
+
External software may modify an NNF file by removing arcs, but it may not update the header.
|
|
131
|
+
Although the resulting file is not conformant, it is still parsable (by ignoring the header).
|
|
132
|
+
Parameter `check_header` can be set to true, which causes an exception being raised if the
|
|
133
|
+
header disagrees with the rest of the file.
|
|
108
134
|
|
|
109
135
|
Args:
|
|
110
136
|
input_stream: to parse, as per `ParserInput` argument.
|
|
137
|
+
var_literals: an optional mapping from literal code to existing circuit variable index.
|
|
111
138
|
const_literals: an optional mapping from literal code to constant value.
|
|
112
139
|
circuit: an optional empty circuit to reuse.
|
|
113
140
|
check_header: if true, an exception is raised if the number of nodes or arcs is not as expected.
|
|
114
141
|
optimise_ops: if true then circuit optimised operations will be used.
|
|
115
142
|
|
|
116
143
|
Returns:
|
|
117
|
-
(circuit_top,
|
|
144
|
+
(circuit_top, var_literals)
|
|
118
145
|
circuit_top: is the resulting top node from parsing the input.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
Assumes:
|
|
122
|
-
If a circuit is provided, it is empty.
|
|
146
|
+
var_literals: is a mapping from literal code (int) to a circuit variable index (int).
|
|
123
147
|
"""
|
|
124
148
|
if circuit is None:
|
|
125
149
|
circuit: Circuit = Circuit()
|
|
126
150
|
|
|
127
|
-
if
|
|
128
|
-
|
|
151
|
+
if var_literals is None:
|
|
152
|
+
var_literals: Dict[int, int] = {}
|
|
129
153
|
|
|
130
154
|
if const_literals is None:
|
|
131
155
|
const_literals: Dict[int, ConstValue] = {}
|
|
132
156
|
|
|
133
|
-
parser = CircuitParser(circuit, check_header, const_literals, optimise_ops)
|
|
157
|
+
parser = CircuitParser(circuit, check_header, var_literals, const_literals, optimise_ops)
|
|
134
158
|
parser.parse(input_stream)
|
|
135
159
|
|
|
136
160
|
nodes = parser.nodes
|
|
137
161
|
cct_top = circuit.zero if len(nodes) == 0 else nodes[-1]
|
|
138
162
|
|
|
139
|
-
return cct_top,
|
|
163
|
+
return cct_top, var_literals
|
|
140
164
|
|
|
141
165
|
|
|
142
166
|
class Parser(ABC):
|
|
@@ -188,12 +212,14 @@ class Parser(ABC):
|
|
|
188
212
|
self.add_node(raise_f, args)
|
|
189
213
|
else:
|
|
190
214
|
self.mul_node(raise_f, args)
|
|
215
|
+
else:
|
|
216
|
+
raise_f(f'unexpected parser state: {state}')
|
|
191
217
|
line = input_stream.readline()
|
|
192
218
|
self.done(raise_f)
|
|
193
219
|
except ParseError as e:
|
|
194
220
|
raise e
|
|
195
221
|
except Exception as e:
|
|
196
|
-
input_stream.raise_error(e)
|
|
222
|
+
input_stream.raise_error(str(e))
|
|
197
223
|
|
|
198
224
|
@abstractmethod
|
|
199
225
|
def comment(self, raise_f, message: str) -> None:
|
|
@@ -226,17 +252,18 @@ class CircuitParser(Parser):
|
|
|
226
252
|
self,
|
|
227
253
|
circuit: Circuit,
|
|
228
254
|
check_header: bool,
|
|
255
|
+
var_literals: Dict[int, int],
|
|
229
256
|
const_literals: Dict[int, ConstValue],
|
|
230
257
|
optimise_ops: bool,
|
|
231
258
|
):
|
|
232
259
|
self.check_header: bool = check_header
|
|
233
|
-
self.
|
|
234
|
-
self.optimise_ops = optimise_ops
|
|
235
|
-
self.circuit = circuit
|
|
260
|
+
self.var_literals: Dict[int, int] = var_literals
|
|
236
261
|
self.const_literals: Dict[int, ConstValue] = const_literals
|
|
262
|
+
self.optimise_ops: bool = optimise_ops
|
|
263
|
+
self.circuit: Circuit = circuit
|
|
237
264
|
self.nodes: List[CircuitNode] = []
|
|
238
|
-
self.num_nodes = None
|
|
239
|
-
self.num_edges = None
|
|
265
|
+
self.num_nodes = None # read from the file header for checking
|
|
266
|
+
self.num_edges = None # read from the file header for checking
|
|
240
267
|
|
|
241
268
|
def comment(self, raise_f, message: str) -> None:
|
|
242
269
|
pass
|
|
@@ -247,13 +274,20 @@ class CircuitParser(Parser):
|
|
|
247
274
|
"""
|
|
248
275
|
const_value: Optional[ConstValue] = self.const_literals.get(literal_code)
|
|
249
276
|
if const_value is not None:
|
|
277
|
+
# Literal code maps to a constant value
|
|
278
|
+
if literal_code in self.var_literals:
|
|
279
|
+
raise_f('literal code both constant and variable: {literal_code}')
|
|
250
280
|
node: ConstNode = self.circuit.const(const_value)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
281
|
+
|
|
282
|
+
elif (var_idx := self.var_literals.get(literal_code)) is not None:
|
|
283
|
+
# Literal code maps to an existing circuit variable
|
|
284
|
+
node: VarNode = self.circuit.vars[var_idx]
|
|
285
|
+
|
|
254
286
|
else:
|
|
287
|
+
# Literal code maps to a new circuit variable
|
|
255
288
|
node: VarNode = self.circuit.new_var()
|
|
256
|
-
self.
|
|
289
|
+
self.var_literals[literal_code] = node.idx
|
|
290
|
+
|
|
257
291
|
self.nodes.append(node)
|
|
258
292
|
|
|
259
293
|
def add_node(self, raise_f, args: List[int]) -> None:
|
ck/in_out/parser_utils.py
CHANGED
ck/pgm_compiler/ace/ace.py
CHANGED
|
@@ -14,6 +14,7 @@ from ck.in_out.render_net import render_bayesian_network
|
|
|
14
14
|
from ck.pgm import PGM
|
|
15
15
|
from ck.pgm_circuit import PGMCircuit
|
|
16
16
|
from ck.pgm_circuit.slot_map import SlotMap
|
|
17
|
+
from ck.utils.local_config import config
|
|
17
18
|
from ck.utils.np_extras import NDArrayFloat64
|
|
18
19
|
from ck.utils.tmp_dir import tmp_dir
|
|
19
20
|
|
|
@@ -101,16 +102,13 @@ def compile_pgm(
|
|
|
101
102
|
node_names: List[str] = render_bayesian_network(pgm, file, check_structure_bayesian=False)
|
|
102
103
|
|
|
103
104
|
# Run Ace
|
|
104
|
-
ace_result: subprocess.CompletedProcess = subprocess.run(ace_cmd, capture_output=
|
|
105
|
-
if print_output:
|
|
106
|
-
print(ace_result.stdout)
|
|
107
|
-
print(ace_result.stderr)
|
|
105
|
+
ace_result: subprocess.CompletedProcess = subprocess.run(ace_cmd, capture_output=(not print_output), text=True)
|
|
108
106
|
if ace_result.returncode != 0:
|
|
109
107
|
raise subprocess.CalledProcessError(
|
|
110
108
|
returncode=ace_result.returncode,
|
|
111
109
|
cmd=' '.join(ace_cmd),
|
|
112
|
-
output=ace_result.stdout,
|
|
113
|
-
stderr=ace_result.stderr,
|
|
110
|
+
output=None if print_output else ace_result.stdout,
|
|
111
|
+
stderr=None if print_output else ace_result.stderr,
|
|
114
112
|
)
|
|
115
113
|
|
|
116
114
|
# Parse the literal map output from Ace
|
|
@@ -124,8 +122,9 @@ def compile_pgm(
|
|
|
124
122
|
parameter_values: NDArrayFloat64
|
|
125
123
|
circuit_top, slot_map, parameter_values = read_nnf_with_literal_map(
|
|
126
124
|
file,
|
|
125
|
+
indicators=pgm.indicators,
|
|
127
126
|
literal_map=literal_map,
|
|
128
|
-
const_parameters=const_parameters
|
|
127
|
+
const_parameters=const_parameters,
|
|
129
128
|
)
|
|
130
129
|
|
|
131
130
|
# Consistency checking
|
|
@@ -201,8 +200,12 @@ def copy_ace_to_default_location(
|
|
|
201
200
|
def default_ace_location() -> Path:
|
|
202
201
|
"""
|
|
203
202
|
Get the default location for Ace files.
|
|
203
|
+
|
|
204
|
+
This function checks the local config for the variable
|
|
205
|
+
CK_ACE_LOCATION. If that is not available, then the
|
|
206
|
+
directory that this Python module is in will be used.
|
|
204
207
|
"""
|
|
205
|
-
return Path(__file__).parent
|
|
208
|
+
return Path(config.get('CK_ACE_LOCATION', Path(__file__).parent))
|
|
206
209
|
|
|
207
210
|
|
|
208
211
|
@dataclass
|
|
@@ -21,7 +21,7 @@ def compile_pgm(
|
|
|
21
21
|
*,
|
|
22
22
|
algorithm: JoinTreeAlgorithm = MIN_FILL_THEN_DEGREE,
|
|
23
23
|
limit_product_tree_search: int = DEFAULT_PRODUCT_SEARCH_LIMIT,
|
|
24
|
-
pre_prune_factor_tables: bool =
|
|
24
|
+
pre_prune_factor_tables: bool = False,
|
|
25
25
|
) -> PGMCircuit:
|
|
26
26
|
"""
|
|
27
27
|
Compile the PGM to an arithmetic circuit, using factor elimination.
|
|
@@ -60,7 +60,7 @@ def compile_pgm_best_jointree(
|
|
|
60
60
|
const_parameters: bool = True,
|
|
61
61
|
*,
|
|
62
62
|
limit_product_tree_search: int = DEFAULT_PRODUCT_SEARCH_LIMIT,
|
|
63
|
-
pre_prune_factor_tables: bool =
|
|
63
|
+
pre_prune_factor_tables: bool = False,
|
|
64
64
|
) -> PGMCircuit:
|
|
65
65
|
"""
|
|
66
66
|
Try multiple elimination heuristics, and use the join tree that has
|
|
@@ -114,7 +114,7 @@ def join_tree_to_circuit(
|
|
|
114
114
|
join_tree: JoinTree,
|
|
115
115
|
const_parameters: bool = True,
|
|
116
116
|
limit_product_tree_search: int = DEFAULT_PRODUCT_SEARCH_LIMIT,
|
|
117
|
-
pre_prune_factor_tables: bool =
|
|
117
|
+
pre_prune_factor_tables: bool = False,
|
|
118
118
|
) -> PGMCircuit:
|
|
119
119
|
"""
|
|
120
120
|
Construct a PGMCircuit from a join-tree.
|