compiled-knowledge 4.0.0a21__cp312-cp312-macosx_11_0_arm64.whl → 4.0.0a23__cp312-cp312-macosx_11_0_arm64.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.

Files changed (32) hide show
  1. ck/circuit/_circuit_cy.c +50 -60
  2. ck/circuit/_circuit_cy.cpython-312-darwin.so +0 -0
  3. ck/circuit/_circuit_cy.pyx +1 -1
  4. ck/circuit/_circuit_py.py +1 -1
  5. ck/circuit_compiler/cython_vm_compiler/_compiler.c +179 -170
  6. ck/circuit_compiler/cython_vm_compiler/_compiler.cpython-312-darwin.so +0 -0
  7. ck/circuit_compiler/cython_vm_compiler/_compiler.pyx +3 -3
  8. ck/circuit_compiler/cython_vm_compiler/cython_vm_compiler.py +14 -7
  9. ck/circuit_compiler/interpret_compiler.py +36 -4
  10. ck/circuit_compiler/llvm_compiler.py +4 -4
  11. ck/circuit_compiler/llvm_vm_compiler.py +9 -3
  12. ck/circuit_compiler/support/circuit_analyser/_circuit_analyser_cy.c +1 -1
  13. ck/circuit_compiler/support/circuit_analyser/_circuit_analyser_cy.cpython-312-darwin.so +0 -0
  14. ck/circuit_compiler/support/llvm_ir_function.py +18 -1
  15. ck/pgm.py +1 -1
  16. ck/pgm_circuit/mpe_program.py +2 -2
  17. ck/pgm_circuit/support/compile_circuit.py +4 -7
  18. ck/pgm_compiler/named_pgm_compilers.py +0 -3
  19. ck/pgm_compiler/support/circuit_table/_circuit_table_cy.c +1 -1
  20. ck/pgm_compiler/support/circuit_table/_circuit_table_cy.cpython-312-darwin.so +0 -0
  21. ck/pgm_compiler/support/clusters.py +8 -4
  22. ck/program/raw_program.py +41 -9
  23. ck/utils/np_extras.py +5 -5
  24. ck_demos/ace/simple_ace_demo.py +18 -0
  25. ck_demos/getting_started/__init__.py +0 -0
  26. ck_demos/getting_started/simple_demo.py +18 -0
  27. ck_demos/programs/demo_raw_program_dump.py +17 -0
  28. {compiled_knowledge-4.0.0a21.dist-info → compiled_knowledge-4.0.0a23.dist-info}/METADATA +1 -1
  29. {compiled_knowledge-4.0.0a21.dist-info → compiled_knowledge-4.0.0a23.dist-info}/RECORD +32 -28
  30. {compiled_knowledge-4.0.0a21.dist-info → compiled_knowledge-4.0.0a23.dist-info}/WHEEL +0 -0
  31. {compiled_knowledge-4.0.0a21.dist-info → compiled_knowledge-4.0.0a23.dist-info}/licenses/LICENSE.txt +0 -0
  32. {compiled_knowledge-4.0.0a21.dist-info → compiled_knowledge-4.0.0a23.dist-info}/top_level.txt +0 -0
@@ -38,7 +38,7 @@ DTYPE_TO_CVM_TYPE: Dict[DTypeNumeric, str] = {
38
38
  }
39
39
 
40
40
 
41
- def make_function(analysis: CircuitAnalysis, dtype: DTypeNumeric) -> Tuple[RawProgramFunction, int]:
41
+ def make_function(analysis: CircuitAnalysis, dtype: DTypeNumeric) -> Tuple[RawProgramFunction, int, int]:
42
42
  """
43
43
  Make a RawProgram function that interprets the circuit.
44
44
 
@@ -47,7 +47,7 @@ def make_function(analysis: CircuitAnalysis, dtype: DTypeNumeric) -> Tuple[RawPr
47
47
  dtype: a numpy data type that must be a key in the dictionary, DTYPE_TO_CVM_TYPE.
48
48
 
49
49
  Returns:
50
- (function, number_of_tmps)
50
+ (function, number_of_tmps, number_of_instructions)
51
51
  """
52
52
 
53
53
  cdef Instructions instructions
@@ -128,7 +128,7 @@ def make_function(analysis: CircuitAnalysis, dtype: DTypeNumeric) -> Tuple[RawPr
128
128
  else:
129
129
  raise ValueError(f'cvm_type_name unexpected: {cvm_type_name!r}')
130
130
 
131
- return function, len(analysis.op_to_tmp)
131
+ return function, len(analysis.op_to_tmp), len(analysis.op_nodes)
132
132
 
133
133
  # VM instructions
134
134
  cdef int ADD = circuit.ADD
@@ -48,15 +48,16 @@ class CythonRawProgram(RawProgram):
48
48
  result: Sequence[CircuitNode],
49
49
  dtype: DTypeNumeric,
50
50
  ):
51
- self.in_vars = in_vars
52
- self.result = result
53
-
54
- function, number_of_tmps = _make_function(
51
+ function, number_of_tmps, number_of_instructions = _make_function(
55
52
  var_nodes=in_vars,
56
53
  result_nodes=result,
57
54
  dtype=dtype,
58
55
  )
59
56
 
57
+ self.in_vars = in_vars
58
+ self.result = result
59
+ self.number_of_instructions = number_of_instructions
60
+
60
61
  super().__init__(
61
62
  function=function,
62
63
  dtype=dtype,
@@ -66,6 +67,10 @@ class CythonRawProgram(RawProgram):
66
67
  var_indices=tuple(var.idx for var in in_vars),
67
68
  )
68
69
 
70
+ def dump(self, *, prefix: str = '', indent: str = ' ') -> None:
71
+ super().dump(prefix=prefix, indent=indent)
72
+ print(f'{prefix}number of instructions = {self.number_of_instructions}')
73
+
69
74
  def __getstate__(self):
70
75
  """
71
76
  Support for pickle.
@@ -94,7 +99,7 @@ class CythonRawProgram(RawProgram):
94
99
  self.in_vars = state['in_vars']
95
100
  self.result = state['result']
96
101
 
97
- self.function, _ = _make_function(
102
+ self.function, _, self.number_of_instructions = _make_function(
98
103
  var_nodes=self.in_vars,
99
104
  result_nodes=self.result,
100
105
  dtype=self.dtype,
@@ -105,7 +110,7 @@ def _make_function(
105
110
  var_nodes: Sequence[VarNode],
106
111
  result_nodes: Sequence[CircuitNode],
107
112
  dtype: DTypeNumeric,
108
- ) -> Tuple[RawProgramFunction, int]:
113
+ ) -> Tuple[RawProgramFunction, int, int]:
109
114
  """
110
115
  Make a RawProgram function that interprets the circuit.
111
116
 
@@ -115,7 +120,9 @@ def _make_function(
115
120
  dtype: a numpy data type that must be a key in the dictionary, DTYPE_TO_CVM_TYPE.
116
121
 
117
122
  Returns:
118
- (function, number_of_tmps)
123
+ (function, number_of_tmps, number_of_instructions)
119
124
  """
120
125
  analysis: CircuitAnalysis = analyze_circuit(var_nodes, result_nodes)
126
+ DEBUG = _compiler.make_function(analysis, dtype)
127
+
121
128
  return _compiler.make_function(analysis, dtype)
@@ -1,17 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import ctypes as ct
3
4
  from dataclasses import dataclass
4
5
  from typing import Sequence, Optional, Dict, List, Tuple, Callable
5
6
 
6
7
  import numpy as np
7
- import ctypes as ct
8
8
 
9
+ from .support.circuit_analyser import CircuitAnalysis, analyze_circuit
10
+ from .support.input_vars import InputVars, InferVars, infer_input_vars
9
11
  from ..circuit import Circuit, CircuitNode, VarNode, OpNode, ADD, MUL
10
12
  from ..program.raw_program import RawProgram, RawProgramFunction
11
13
  from ..utils.iter_extras import multiply, first
12
14
  from ..utils.np_extras import NDArrayNumeric, DTypeNumeric
13
- from .support.circuit_analyser import CircuitAnalysis, analyze_circuit
14
- from .support.input_vars import InputVars, InferVars, infer_input_vars
15
15
 
16
16
  # index to a value array
17
17
  _VARS = 0
@@ -85,6 +85,15 @@ class InterpreterRawProgram(RawProgram):
85
85
  var_indices=tuple(var.idx for var in in_vars),
86
86
  )
87
87
 
88
+ def dump(self, *, prefix: str = '', indent: str = ' ', show_instructions: bool = True) -> None:
89
+ super().dump(prefix=prefix, indent=indent)
90
+ print(f'{prefix}number of instructions = {len(self.instructions)}')
91
+ if show_instructions:
92
+ print(f'{prefix}instructions:')
93
+ next_prefix: str = prefix + indent
94
+ for instruction in self.instructions:
95
+ print(f'{next_prefix}{instruction.to_str(self.var_indices, self.np_consts)}')
96
+
88
97
  def __getstate__(self):
89
98
  """
90
99
  Support for pickle.
@@ -123,7 +132,6 @@ def _make_instructions(
123
132
  analysis: CircuitAnalysis,
124
133
  dtype: DTypeNumeric,
125
134
  ) -> Tuple[Sequence[_Instruction], NDArrayNumeric]:
126
-
127
135
  # Store const values in a numpy array
128
136
  node_to_const_idx: Dict[int, int] = {
129
137
  id(node): i
@@ -139,6 +147,7 @@ def _make_instructions(
139
147
  for node_id, const_idx in node_to_const_idx.items():
140
148
  node_to_element[node_id] = _ElementID(_CONSTS, const_idx)
141
149
  # var nodes
150
+ var_node: VarNode
142
151
  for i, var_node in enumerate(analysis.var_nodes):
143
152
  if var_node.is_const():
144
153
  node_to_element[id(var_node)] = node_to_element[id(var_node.const)]
@@ -215,9 +224,32 @@ class _ElementID:
215
224
  array: int # VARS, TMPS, CONSTS, RESULT
216
225
  index: int # index into the array
217
226
 
227
+ def to_str(self, var_indices: Sequence[int], consts: NDArrayNumeric) -> str:
228
+ if self.array == _VARS:
229
+ return f'var[{var_indices[self.index]}]'
230
+ elif self.array == _TMPS:
231
+ return f'tmp[{self.index}]'
232
+ elif self.array == _CONSTS:
233
+ return str(consts.item(self.index))
234
+ elif self.array == _RESULT:
235
+ return f'result[{self.index}]'
236
+ else:
237
+ return f'?[{self.index}]'
238
+
218
239
 
219
240
  @dataclass
220
241
  class _Instruction:
221
242
  operation: Callable
222
243
  args: Sequence[_ElementID]
223
244
  dest: _ElementID
245
+
246
+ def to_str(self, var_indices: Sequence[int], consts: NDArrayNumeric) -> str:
247
+ symbol: str
248
+ if self.operation is multiply:
249
+ symbol = 'mul'
250
+ elif self.operation == sum:
251
+ symbol = 'sum'
252
+ else:
253
+ symbol = '<?>'
254
+ args: str = ' '.join(elem.to_str(var_indices, consts) for elem in self.args)
255
+ return f'{self.dest.to_str(var_indices, consts)} = {symbol} {args}'
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from enum import Enum
5
- from typing import Sequence, Optional, Tuple, Dict, Protocol
5
+ from typing import Sequence, Optional, Tuple, Dict, Protocol, assert_never
6
6
 
7
7
  import llvmlite.binding as llvm
8
8
  import llvmlite.ir as ir
@@ -238,7 +238,7 @@ class _FunctionBuilderTmps(_FunctionBuilder):
238
238
  if idx is not None:
239
239
  return builder.load(builder.gep(self.out_args, [ir.Constant(self.llvm_idx_type, idx)]))
240
240
 
241
- assert False, 'not reached'
241
+ assert_never('not reached')
242
242
 
243
243
  def store_calculation(self, value: ir.Value, op_node: OpNode) -> None:
244
244
  """
@@ -262,7 +262,7 @@ class _FunctionBuilderTmps(_FunctionBuilder):
262
262
  builder.store(value, ptr)
263
263
  return
264
264
 
265
- assert False, 'not reached'
265
+ assert_never('not reached')
266
266
 
267
267
  def store_result(self, value: ir.Value, idx: int) -> None:
268
268
  """
@@ -359,7 +359,7 @@ class _FunctionBuilderStack(_FunctionBuilder):
359
359
  if idx is not None:
360
360
  return builder.load(builder.gep(self.out_args, [ir.Constant(self.llvm_idx_type, idx)]))
361
361
 
362
- assert False, 'not reached'
362
+ assert_never('not reached')
363
363
 
364
364
  def store_calculation(self, value: ir.Value, op_node: OpNode) -> None:
365
365
  """
@@ -1,12 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import ctypes as ct
3
4
  from dataclasses import dataclass
4
5
  from typing import Sequence, Optional, Tuple, List, Dict
5
6
 
6
7
  import llvmlite.binding as llvm
7
8
  import llvmlite.ir as ir
8
9
  import numpy as np
9
- import ctypes as ct
10
10
 
11
11
  from .support.circuit_analyser import CircuitAnalysis, analyze_circuit
12
12
  from .support.input_vars import InputVars, InferVars, infer_input_vars
@@ -126,6 +126,12 @@ class LLVMRawProgramWithArrays(LLVMRawProgram):
126
126
  instructions: np.ndarray
127
127
  consts: np.ndarray
128
128
 
129
+ def dump(self, *, prefix: str = '', indent: str = ' ', show_instructions: bool = True) -> None:
130
+ super().dump(prefix=prefix, indent=indent)
131
+ print(f'{prefix}LLVM byte code size = {len(self.instructions)}')
132
+ if show_instructions:
133
+ self.dump_llvm_program(prefix=prefix, indent=indent)
134
+
129
135
  def __post_init__(self):
130
136
  self._set_globals(self.instructions, _SET_INSTRUCTIONS_FUNCTION_NAME)
131
137
  self._set_globals(self.consts, _SET_CONSTS_FUNCTION_NAME)
@@ -200,7 +206,7 @@ def _make_llvm_program(
200
206
  consts_global.global_constant = True
201
207
  consts_global.initializer = ir.Constant(consts_array_type, const_values)
202
208
  data_idx_0 = ir.Constant(data_idx_type, 0)
203
- consts: ir.Value = builder.gep(consts_global, [data_idx_0, data_idx_0])
209
+ consts: ir.Value = builder.gep(consts_global, [data_idx_0, data_idx_0])
204
210
 
205
211
  # Put bytecode into the LLVM module
206
212
  instructions_array_type = ir.ArrayType(byte_type, len(byte_code))
@@ -218,7 +224,7 @@ def _make_llvm_program(
218
224
 
219
225
  instructions_ptr_type = byte_type.as_pointer()
220
226
  instructions_global = ir.GlobalVariable(module, instructions_ptr_type, name='instructions')
221
- instructions_global.initializer =ir.Constant(instructions_ptr_type, None)
227
+ instructions_global.initializer = ir.Constant(instructions_ptr_type, None)
222
228
  instructions: ir.Value = builder.load(instructions_global)
223
229
 
224
230
  interp = _InterpBuilder(builder, type_info, inst_idx_type, data_idx_bytes, num_args_bytes, consts, instructions)
@@ -15,7 +15,7 @@
15
15
  "-O3"
16
16
  ],
17
17
  "include_dirs": [
18
- "/private/var/folders/y6/nj790rtn62lfktb1sh__79hc0000gn/T/build-env-uu_n4tdu/lib/python3.12/site-packages/numpy/_core/include"
18
+ "/private/var/folders/y6/nj790rtn62lfktb1sh__79hc0000gn/T/build-env-x_3zb_va/lib/python3.12/site-packages/numpy/_core/include"
19
19
  ],
20
20
  "name": "ck.circuit_compiler.support.circuit_analyser._circuit_analyser_cy",
21
21
  "sources": [
@@ -1,7 +1,7 @@
1
1
  import ctypes as ct
2
2
  from dataclasses import dataclass
3
3
  from enum import Enum
4
- from typing import Callable, Tuple, Optional
4
+ from typing import Callable, Tuple, Optional, List
5
5
 
6
6
  import llvmlite.binding as llvm
7
7
  import llvmlite.ir as ir
@@ -172,6 +172,23 @@ class LLVMRawProgram(RawProgram):
172
172
  'opt': self.opt,
173
173
  }
174
174
 
175
+ def dump(self, *, prefix: str = '', indent: str = ' ', show_instructions: bool = True) -> None:
176
+ super().dump(prefix=prefix, indent=indent)
177
+ print(f'{prefix}optimisation level = {self.opt}')
178
+ if show_instructions:
179
+ self.dump_llvm_program(prefix=prefix, indent=indent)
180
+
181
+ def dump_llvm_program(self, *, prefix: str = '', indent: str = ' ') -> None:
182
+ if self.llvm_program is None:
183
+ print(f'{prefix}LLVM program: unavailable')
184
+ else:
185
+ llvm_program: List[str] = self.llvm_program.split('\n')
186
+ print(f'{prefix}LLVM program size = {len(llvm_program)}')
187
+ print(f'{prefix}LLVM program:')
188
+ next_prefix: str = prefix + indent
189
+ for line in llvm_program:
190
+ print(f'{next_prefix}{line}')
191
+
175
192
  def __setstate__(self, state):
176
193
  """
177
194
  Support for pickle.
ck/pgm.py CHANGED
@@ -2935,7 +2935,7 @@ class CPTPotentialFunction(PotentialFunction):
2935
2935
  return True
2936
2936
  else:
2937
2937
  # The requested tolerance is tighter than ensured.
2938
- # Need to use the default method
2938
+ # Need to use the default method.
2939
2939
  return super().is_cpt(tolerance)
2940
2940
 
2941
2941
  @property
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from functools import partial
5
- from typing import Sequence, Optional, Tuple, List, Dict, Set
5
+ from typing import Sequence, Optional, Tuple, List, Dict, Set, assert_never
6
6
 
7
7
  from ck.circuit import CircuitNode, Circuit, VarNode, OpNode, ADD, MUL
8
8
  from ck.circuit_compiler import llvm_vm_compiler, CircuitCompiler
@@ -207,7 +207,7 @@ class MPEProgram(ProgramWithSlotmap):
207
207
  self._trace_var(child, states)
208
208
  return
209
209
  # No child value equaled the value for node! We should never get here
210
- assert False, 'not reached'
210
+ assert_never('not reached')
211
211
  elif node.symbol == MUL:
212
212
  # Recurse though each child node
213
213
  for child in node.args:
@@ -2,7 +2,6 @@ from typing import Optional, Sequence
2
2
 
3
3
  from ck.circuit import CircuitNode, TmpConst, Circuit
4
4
  from ck.circuit_compiler import CircuitCompiler
5
- from ck.circuit_compiler.llvm_compiler import DataType, DEFAULT_TYPE_INFO, compile_circuit
6
5
  from ck.circuit_compiler import DEFAULT_CIRCUIT_COMPILER
7
6
  from ck.pgm_circuit import PGMCircuit
8
7
  from ck.program import RawProgram
@@ -48,7 +47,7 @@ def compile_param_derivatives(
48
47
  pgm_circuit: PGMCircuit,
49
48
  self_multiply: bool = False,
50
49
  params_value: Optional[float | int] = 1,
51
- data_type: DataType = DEFAULT_TYPE_INFO,
50
+ compiler: CircuitCompiler = DEFAULT_CIRCUIT_COMPILER,
52
51
  ) -> RawProgram:
53
52
  """
54
53
  Compile the circuit to a program for computing the partial derivatives of the parameters.
@@ -56,14 +55,12 @@ def compile_param_derivatives(
56
55
 
57
56
  Typically, this will grow the circuit by the addition of circuit nodes to compute the derivatives.
58
57
 
59
- This uses the LLVM circuit compiler.
60
-
61
58
  Args:
62
59
  pgm_circuit: The circuit (and PGM) that will be compiled to a program.
63
60
  self_multiply: if true then each partial derivative df/dx will be multiplied by x.
64
61
  params_value: if not None, then circuit vars representing parameters will be temporarily
65
62
  set to this value for compiling the program. Default is 1.
66
- data_type: What data type to use for arithmetic calculations. Either a DataType member or TypeInfo.
63
+ compiler: function from circuit nodes to raw program.
67
64
  """
68
65
  top: CircuitNode = pgm_circuit.circuit_top
69
66
  circuit: Circuit = top.circuit
@@ -76,8 +73,8 @@ def compile_param_derivatives(
76
73
  if params_value is not None:
77
74
  with TmpConst(circuit) as tmp:
78
75
  tmp.set_const(param_vars, params_value)
79
- raw_program: RawProgram = compile_circuit(*derivatives, circuit=circuit, data_type=data_type)
76
+ raw_program: RawProgram = compiler(*derivatives, circuit=circuit)
80
77
  else:
81
- raw_program: RawProgram = compile_circuit(*derivatives, circuit=circuit, data_type=data_type)
78
+ raw_program: RawProgram = compiler(*derivatives, circuit=circuit)
82
79
 
83
80
  return raw_program
@@ -16,7 +16,6 @@ class NamedPGMCompiler(Enum):
16
16
  Wrapping in a tuple is needed otherwise Python erases the type of the member, which can cause problems.
17
17
  Each member itself is callable, confirming to the PGMCompiler protocol, delegating to the compiler function.
18
18
  """
19
- # @formatter:off
20
19
 
21
20
  VE_MIN_DEGREE = _get_compiler_algorithm(variable_elimination, 'MIN_DEGREE')
22
21
  VE_MIN_DEGREE_THEN_FILL = _get_compiler_algorithm(variable_elimination, 'MIN_DEGREE_THEN_FILL')
@@ -45,8 +44,6 @@ class NamedPGMCompiler(Enum):
45
44
 
46
45
  ACE = _get_compiler(ace)
47
46
 
48
- # @formatter:on
49
-
50
47
  def __call__(self, pgm: PGM, const_parameters: bool = True) -> PGMCircuit:
51
48
  """
52
49
  Each member of the enum is a PGMCompiler function.
@@ -15,7 +15,7 @@
15
15
  "-O3"
16
16
  ],
17
17
  "include_dirs": [
18
- "/private/var/folders/y6/nj790rtn62lfktb1sh__79hc0000gn/T/build-env-uu_n4tdu/lib/python3.12/site-packages/numpy/_core/include"
18
+ "/private/var/folders/y6/nj790rtn62lfktb1sh__79hc0000gn/T/build-env-x_3zb_va/lib/python3.12/site-packages/numpy/_core/include"
19
19
  ],
20
20
  "name": "ck.pgm_compiler.support.circuit_table._circuit_table_cy",
21
21
  "sources": [
@@ -295,7 +295,7 @@ class Clusters:
295
295
  Returns:
296
296
  the maximum `len(cluster)` over all clusters.
297
297
  """
298
- return max(len(cluster) for cluster in self.clusters)
298
+ return max((len(cluster) for cluster in self.clusters), default=0)
299
299
 
300
300
  def max_cluster_weighted_size(self, rv_log_sizes: Sequence[float]) -> float:
301
301
  """
@@ -303,13 +303,17 @@ class Clusters:
303
303
 
304
304
  Args:
305
305
  rv_log_sizes: is an array of random variable sizes, such that
306
- for a random variable `rv`, `rv_log_sizes[rv.idx] = log2(len(rv))`.
306
+ for a random variable `rv`, `rv_log_sizes[rv.idx] = log2(len(rv))`,
307
+ e.g., `self.pgm.rv_log_sizes`.
307
308
  Returns:
308
309
  the maximum `sum(rv_log_sizes[rv_idx] for rv_idx in cluster)` over all clusters.
309
310
  """
310
311
  return max(
311
- sum(rv_log_sizes[rv_idx] for rv_idx in cluster)
312
- for cluster in self.clusters
312
+ (
313
+ sum(rv_log_sizes[rv_idx] for rv_idx in cluster)
314
+ for cluster in self.clusters
315
+ ),
316
+ default=0
313
317
  )
314
318
 
315
319
  def eliminate(self, rv_index: int) -> None:
ck/program/raw_program.py CHANGED
@@ -21,8 +21,11 @@ class RawProgram:
21
21
  A raw program is returned by a circuit compiler to provide execution of
22
22
  the function defined by a compiled circuit.
23
23
 
24
- A `RawProgram` is a `Callable` with the signature:
25
-
24
+ A `RawProgram` is a `Callable`; given an array of input variable values,
25
+ return a numpy array of result values. Calling a RawProgram is not necessarily
26
+ an efficient method for executing a program as buffers are reallocated for
27
+ each call. Alternatively, a `RawProgram` can be wrapped in a `ProgramBuffer`
28
+ for computationally efficient memory buffer reuse.
26
29
 
27
30
  Fields:
28
31
  function: is a function of three ctypes arrays, returning nothing.
@@ -30,7 +33,7 @@ class RawProgram:
30
33
  number_of_vars: the number of input values (first function argument).
31
34
  number_of_tmps: the number of working memory values (second function argument).
32
35
  number_of_results: the number of result values (third function argument).
33
- var_indices: maps the index of inputs (from 0 to self.number_of_vars - 1) to the index
36
+ var_indices: maps the index of inputs (from 0 to self.number_of_vars - 1) to the index
34
37
  of the corresponding circuit var.
35
38
  """
36
39
 
@@ -41,19 +44,33 @@ class RawProgram:
41
44
  number_of_results: int
42
45
  var_indices: Sequence[int]
43
46
 
44
- def __call__(self, in_vars: NDArrayNumeric | Sequence[int | float]) -> NDArrayNumeric:
47
+ def __call__(self, var_values: NDArrayNumeric | Sequence[int | float] | int | float) -> NDArrayNumeric:
45
48
  """
46
- Call the raw program as a function from an array to an array.
49
+ Call the raw program as a function. This method will allocate numpy arrays of type `self.dtype`
50
+ for input, temporary, and output values. If `var_values` is a numpy array of the needed
51
+ dtype then it will be used directly.
52
+
53
+ Args:
54
+ var_values: the input variable values. This can be a numpy array, a Python sequence of
55
+ floats or int, or a single float or int. The number of input values must equal
56
+ `self.number_of_vars`.
57
+
58
+ Returns:
59
+ a numpy array of result values with shape `(self.number_of_results,)`.
47
60
  """
48
61
  array_vars: NDArrayNumeric
49
62
  if isinstance(vars, np.ndarray):
50
- array_vars = in_vars
63
+ if var_values.dtype != self.dtype:
64
+ array_vars = var_values.astype(self.dtype)
65
+ else:
66
+ array_vars = self.dtype
67
+ elif isinstance(vars, (int, float)):
68
+ array_vars = np.array([var_values], dtype=self.dtype)
51
69
  else:
52
- array_vars = np.array(in_vars, dtype=self.dtype)
70
+ array_vars = np.array(var_values, dtype=self.dtype)
71
+
53
72
  if array_vars.shape != (self.number_of_vars,):
54
73
  raise ValueError(f'input array incorrect shape: got {array_vars.shape} expected ({self.number_of_vars},)')
55
- if array_vars.dtype != self.dtype:
56
- raise ValueError(f'input array incorrect dtype: got {array_vars.dtype} expected {self.dtype}')
57
74
 
58
75
  array_tmps: NDArrayNumeric = np.zeros(self.number_of_tmps, dtype=self.dtype)
59
76
  array_outs: NDArrayNumeric = np.zeros(self.number_of_results, dtype=self.dtype)
@@ -65,3 +82,18 @@ class RawProgram:
65
82
 
66
83
  self.function(c_array_vars, c_array_tmps, c_array_outs)
67
84
  return array_outs
85
+
86
+ def dump(self, *, prefix: str = '', indent: str = ' ') -> None:
87
+ """
88
+ Print a dump of the PGM.
89
+ This is intended for demonstration and debugging purposes.
90
+
91
+ Args:
92
+ prefix: optional prefix for indenting all lines.
93
+ indent: additional prefix to use for extra indentation.
94
+ """
95
+ print(f'{prefix}{self.__class__.__name__}')
96
+ print(f'{prefix}signature = [{self.number_of_vars}] -> [{self.number_of_results}]')
97
+ print(f'{prefix}temps = {self.number_of_tmps}')
98
+ print(f'{prefix}dtype = {self.dtype}')
99
+ print(f'{prefix}var_indices = {self.var_indices}')
ck/utils/np_extras.py CHANGED
@@ -29,17 +29,17 @@ NDArrayNumeric = NDArray[DTypeNumeric]
29
29
 
30
30
 
31
31
  # Constants for maximum number of states.
32
- _MAX_STATES_8: int = 2 ** 8 - 1
33
- _MAX_STATES_16: int = 2 ** 16 - 1
34
- _MAX_STATES_32: int = 2 ** 32 - 1
35
- _MAX_STATES_64: int = 2 ** 64 - 1
32
+ _MAX_STATES_8: int = 2 ** 8
33
+ _MAX_STATES_16: int = 2 ** 16
34
+ _MAX_STATES_32: int = 2 ** 32
35
+ _MAX_STATES_64: int = 2 ** 64
36
36
 
37
37
 
38
38
  def dtype_for_number_of_states(number_of_states: int) -> DTypeStates:
39
39
  """
40
40
  Infer the numpy dtype required to store any state index of the given PGM.
41
41
  """
42
- # Infer the needed size of
42
+ # Infer the needed dtype required to hold a number of states
43
43
  if number_of_states <= _MAX_STATES_8:
44
44
  return np.uint8
45
45
  if number_of_states <= _MAX_STATES_16:
@@ -0,0 +1,18 @@
1
+ from ck import example
2
+ from ck.pgm import RVMap
3
+ from ck.pgm_circuit.wmc_program import WMCProgram
4
+ from ck.pgm_compiler import NamedPGMCompiler
5
+
6
+ # create the example "Cancer" Bayesian network
7
+ pgm = example.Cancer()
8
+
9
+ # compile the PGM and construct an object for probabilistic queries
10
+ wmc = WMCProgram(NamedPGMCompiler.ACE(pgm))
11
+
12
+ # provide easy access to the random variables - not needed but simplifies this demo
13
+ rvs = RVMap(pgm)
14
+
15
+ # get the probability of having cancer given that pollution is high
16
+ pr = wmc.probability(rvs.cancer('True'), condition=rvs.pollution('high'))
17
+
18
+ print('probability of having cancer given that pollution is high =', pr)
File without changes
@@ -0,0 +1,18 @@
1
+ from ck import example
2
+ from ck.pgm import RVMap
3
+ from ck.pgm_circuit.wmc_program import WMCProgram
4
+ from ck.pgm_compiler import DEFAULT_PGM_COMPILER
5
+
6
+ # create the example "Cancer" Bayesian network
7
+ pgm = example.Cancer()
8
+
9
+ # compile the PGM and construct an object for probabilistic queries
10
+ wmc = WMCProgram(DEFAULT_PGM_COMPILER(pgm))
11
+
12
+ # provide easy access to the random variables - not needed but simplifies this demo
13
+ rvs = RVMap(pgm)
14
+
15
+ # get the probability of having cancer given that pollution is high
16
+ pr = wmc.probability(rvs.cancer('True'), condition=rvs.pollution('high'))
17
+
18
+ print('probability of having cancer given that pollution is high =', pr)
@@ -0,0 +1,17 @@
1
+ from ck.circuit import Circuit
2
+ from ck.circuit_compiler import NamedCircuitCompiler
3
+
4
+
5
+ def main() -> None:
6
+ cct = Circuit()
7
+ a, b, c, d = cct.new_vars(4)
8
+ top = a * b + c * d + 56.23
9
+
10
+ for compiler in NamedCircuitCompiler:
11
+ raw_program = compiler(top)
12
+ raw_program.dump()
13
+ print()
14
+
15
+
16
+ if __name__ == '__main__':
17
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: compiled-knowledge
3
- Version: 4.0.0a21
3
+ Version: 4.0.0a23
4
4
  Summary: A Python package for compiling and querying discrete probabilistic graphical models.
5
5
  Author-email: Barry Drake <barry@compiledknowledge.org>
6
6
  License-Expression: MIT