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.

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: CircuitCompiler = (partial(llvm_compiler.compile_circuit, flavour=Flavour.STACK),)
22
- LLVM_TMPS: CircuitCompiler = (partial(llvm_compiler.compile_circuit, flavour=Flavour.TMPS, opt=0),)
23
- LLVM_VM: CircuitCompiler = (llvm_vm_compiler.compile_circuit,)
24
- CYTHON_VM: CircuitCompiler = (cython_vm_compiler.compile_circuit,)
25
- INTERPRET: CircuitCompiler = (interpret_compiler.compile_circuit,)
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: CircuitCompiler = (partial(llvm_compiler.compile_circuit, flavour=Flavour.FUNCS, opt=0),)
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: CircuitCompiler = (partial(llvm_vm_compiler.compile_circuit, compile_arrays=True),)
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
- 'AIDS early', 'AILD', 'ALIP', 'Cat scratch disease', 'Dermatopathic laden', 'Florid follic hyperp',
11
- 'GLH hyaline vascular', 'GLH plasma cell', 'Granulomatous laden', 'Histiocytosis x', 'Infectious mono',
12
- 'Leprosy-lepromatous', 'Lymphangiographic', 'Mantle zone hyperpla', 'Necrotizing Kikuchi',
13
- 'Necrotiz non-Kikuchi', 'Rheumatoid arthritis', 'Sarcoidosis', 'SHML', 'Sinus histiocytosis', 'Syphilis',
14
- 'Toxoplasmosis', 'Tuberculosis', 'Viral NOS', "Whipple's disease", 'L&H nodular HD', 'L&H diffuse HD',
15
- 'Nodular sclerosis HD', 'Cellular phase NSHD', 'Syncytial NSHD', 'Mixed cellularity HD', 'Interfollicular HD',
16
- 'Diffuse fibrosis HD', 'Reticular-type HD', 'Small cleaved fol', 'Mixed fol', 'Large cell fol',
17
- 'Small noncleaved fol', 'Small lymphocytic', 'Plasmacytoid lyctic', 'Mantle zone lymphoma', 'Small cleaved dif',
18
- 'Mixed FCC dif', 'Large cell dif', 'B-immunoblastic', 'T-immunob mixed', 'AILD-like T-cell lym', 'Japanese ATL',
19
- 'Lymphoblastic', 'Small noncleaved dif', 'True histiocytic', 'Ki-1 LC anaplas T', 'Multiple myeloma',
20
- 'Mycosis fungoides', 'AML', 'Hairy cell leukemia', 'Carcinoma', 'Melanoma', 'EM plasmacytoma',
21
- "Kaposi's sarcoma", 'Mast-cell disease', 'AIDS involutionary', 'T-immunob large', 'Monocytoid B-cell',
22
- 'Nasopharyngeal CA', 'Seminoma', 'SLE', 'Mycobact histiocytos', 'Mast-cell hyperplas', 'LGV', 'Histoplasmosis',
23
- 'Coccidioidomycosis', 'CMV', 'Brucellosis', 'Ki-1 LC anaplas B', 'Intermed lymphocytic'))
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
- 'Absent [0]', 'Rare [1-5]', 'Few [6-25]', 'Many [26 - 100]', 'Striking [>100]', 'Sheets [>50pc of section]'))
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
- 'No LLCs', 'Multilobated [some]', 'Cerebriform/Mulberry [some]', 'Convoluted [some]', 'Irregular [most]',
67
- 'Round/slightly irregular [most]'))
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
- 'Absent [0pc]', 'Sparse [<10pc]', 'Moderate [11-50pc]', 'Numerous [51-90pc]', 'Striking [>90pc]'))
78
+ 'Absent [0pc]', 'Sparse [<10pc]', 'Moderate [11-50pc]', 'Numerous [51-90pc]', 'Striking [>90pc]'))
70
79
  pgm_rv41 = self.new_rv('F_POPU', (
71
- 'No follicles', '>80pc 6-12u', '>50pc 13-20u with nucleoli', '>50pc >20u with nucleoli', 'Other'))
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
- 'Absent [0pc]', 'Sparse [<10pc]', 'Moderate [11-50pc]', 'Numerous [51-90pc]', 'Striking [>90pc]'))
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
- 'No MLCs', 'Cerebriform [some]', 'Convoluted [some]', 'Irregular [most]', 'Round/slightly irregular [most]'))
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
- 'Absent [0pc]', 'Few [<5pc]', 'Moderate [6-20pc]', 'Marked [21-50pc]', 'Striking [51-90pc]', 'Sheets [>90pc]'))
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
- 'No SLCs', 'Cerebriform [some]', 'Convoluted [some]', 'Moderate-mark irregular [most]',
105
- 'Round/slightly irregular [most]'))
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
- 'Absent [0pc]', 'Sparse [1-10pc]', 'Moderate [11-50pc]', 'Numerous [51-90pc]', 'Striking [>90pc]'))
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
- 'No follicles/no mantle zones', 'Mantle zones absent in most follicles', 'Most incompletely surround follicles',
127
- 'Most completely surround fol not thick', 'Most thick [>10 lymphocytes]'))
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
- 'No follicles', 'Almost all well defined', 'Both well and poor defined', 'Almost all poor defined'))
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
- 'No follicles/no mantle zones', 'Round', 'Slightly irregular', 'Moderately irregular', 'Markedly irregular'))
184
+ 'No follicles/no mantle zones', 'Round', 'Slightly irregular', 'Moderately irregular',
185
+ 'Markedly irregular'))
173
186
  pgm_rv131 = self.new_rv('FMZ_FUS', (
174
- 'No thick mantle zones', 'No nodules no fusion', 'Nodules but no fusion', 'Fusion with or without nodules'))
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
- 'No SLCs', 'None identifiable', '1 or more peripheral small', '1 central medium to large', 'Other'))
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
- 20.0, 1.5, 5.0, 5.0, 25.0,
319
- 25.0, 5.0, 5.0, 15.0, 4.0,
320
- 2.0, 2.0, 5.0, 12.5, 8.0,
321
- 12.8, 2.0, 2.0, 3.0, 20.0,
322
- 2.0, 5.0, 4.0, 15.0, 0.5,
323
- 15.0, 2.5, 90.0, 8.0, 8.0,
324
- 70.0, 5.0, 3.0, 3.0, 110.0,
325
- 55.0, 20.0, 5.0, 40.0, 8.0,
326
- 10.0, 5.0, 6.0, 110.0, 20.0,
327
- 20.0, 12.0, 2.0, 10.0, 80.0,
328
- 1.0, 15.0, 2.0, 3.0, 5.0,
329
- 0.1, 20.0, 8.0, 5.0, 8.0,
330
- 2.0, 20.0, 25.0, 20.0, 5.0,
331
- 2.0, 2.0, 2.0, 2.0, 2.0,
332
- 2.0, 2.0, 5.0, 0.25, 4.0,
333
- 8.0
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()
@@ -108,7 +108,7 @@ class Parser(ABC):
108
108
  except ParseError as e:
109
109
  raise e
110
110
  except Exception as e:
111
- input_stream.raise_error(e)
111
+ input_stream.raise_error(str(e))
112
112
 
113
113
  @abstractmethod
114
114
  def comment(self, raise_f, message: str) -> None:
@@ -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
- top_node, literal_slot_map = read_nnf(
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
- # Build the slot map from indicator to slot
69
- slot_map: Dict[SlotKey, int] = {
70
- indicator: literal_slot_map[literal_code]
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] = literal_slot_map.get(literal_code)
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
- The returned slot map will have entries to get circuit vars for literal codes.
103
- E.g., given a literal code (int), use `circuit.vars[slot_map[literal_code]]` to get its var node.
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
- Software may simplify an NNF file by removing arcs, but it may not update the header.
106
- Although the resulting file is not conformant, it is still parsable.
107
- Parameter `check_header` can be set to false, which prevents an exception being raised.
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, literal_slot_map)
144
+ (circuit_top, var_literals)
118
145
  circuit_top: is the resulting top node from parsing the input.
119
- literal_slot_map: is a mapping from literal code (int) to a circuit var index (int).
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 circuit.number_of_vars != 0:
128
- raise ValueError('the given circuit must be empty')
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, parser.literal_slot_map
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.literal_slot_map: Dict[SlotKey, int] = {}
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
- elif literal_code in self.literal_slot_map.keys():
252
- raise_f(f'duplicated literal code: {literal_code}')
253
- return
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.literal_slot_map[literal_code] = node.idx
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
@@ -32,7 +32,7 @@ class ParserInput:
32
32
  def __init__(self, input_stream):
33
33
  self._input = _check_input(input_stream)
34
34
  self._prev_line_len = 0
35
- self._cur_line = 1
35
+ self._cur_line = 0
36
36
  self._cur_char = 1
37
37
  self._lookahead = []
38
38
 
@@ -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=True, text=True)
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 = True,
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 = True,
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 = True,
117
+ pre_prune_factor_tables: bool = False,
118
118
  ) -> PGMCircuit:
119
119
  """
120
120
  Construct a PGMCircuit from a join-tree.