compiled-knowledge 4.0.0a21__cp313-cp313-win_amd64.whl → 4.0.0a23__cp313-cp313-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_cy.c +50 -60
- ck/circuit/_circuit_cy.cp313-win_amd64.pyd +0 -0
- ck/circuit/_circuit_cy.pyx +1 -1
- ck/circuit/_circuit_py.py +1 -1
- ck/circuit_compiler/cython_vm_compiler/_compiler.c +179 -170
- ck/circuit_compiler/cython_vm_compiler/_compiler.cp313-win_amd64.pyd +0 -0
- ck/circuit_compiler/cython_vm_compiler/_compiler.pyx +3 -3
- ck/circuit_compiler/cython_vm_compiler/cython_vm_compiler.py +14 -7
- ck/circuit_compiler/interpret_compiler.py +36 -4
- ck/circuit_compiler/llvm_compiler.py +4 -4
- ck/circuit_compiler/llvm_vm_compiler.py +9 -3
- ck/circuit_compiler/support/circuit_analyser/_circuit_analyser_cy.c +1 -1
- ck/circuit_compiler/support/circuit_analyser/_circuit_analyser_cy.cp313-win_amd64.pyd +0 -0
- ck/circuit_compiler/support/llvm_ir_function.py +18 -1
- ck/pgm.py +1 -1
- ck/pgm_circuit/mpe_program.py +2 -2
- ck/pgm_circuit/support/compile_circuit.py +4 -7
- ck/pgm_compiler/named_pgm_compilers.py +0 -3
- ck/pgm_compiler/support/circuit_table/_circuit_table_cy.c +1 -1
- ck/pgm_compiler/support/circuit_table/_circuit_table_cy.cp313-win_amd64.pyd +0 -0
- ck/pgm_compiler/support/clusters.py +8 -4
- ck/program/raw_program.py +41 -9
- ck/utils/np_extras.py +5 -5
- ck_demos/ace/simple_ace_demo.py +18 -0
- ck_demos/getting_started/__init__.py +0 -0
- ck_demos/getting_started/simple_demo.py +18 -0
- ck_demos/programs/demo_raw_program_dump.py +17 -0
- {compiled_knowledge-4.0.0a21.dist-info → compiled_knowledge-4.0.0a23.dist-info}/METADATA +1 -1
- {compiled_knowledge-4.0.0a21.dist-info → compiled_knowledge-4.0.0a23.dist-info}/RECORD +32 -28
- {compiled_knowledge-4.0.0a21.dist-info → compiled_knowledge-4.0.0a23.dist-info}/WHEEL +0 -0
- {compiled_knowledge-4.0.0a21.dist-info → compiled_knowledge-4.0.0a23.dist-info}/licenses/LICENSE.txt +0 -0
- {compiled_knowledge-4.0.0a21.dist-info → compiled_knowledge-4.0.0a23.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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)
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"/O2"
|
|
14
14
|
],
|
|
15
15
|
"include_dirs": [
|
|
16
|
-
"C:\\Users\\runneradmin\\AppData\\Local\\Temp\\build-env-
|
|
16
|
+
"C:\\Users\\runneradmin\\AppData\\Local\\Temp\\build-env-ca0w3uvm\\Lib\\site-packages\\numpy\\_core\\include"
|
|
17
17
|
],
|
|
18
18
|
"name": "ck.circuit_compiler.support.circuit_analyser._circuit_analyser_cy",
|
|
19
19
|
"sources": [
|
|
Binary file
|
|
@@ -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
|
ck/pgm_circuit/mpe_program.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
76
|
+
raw_program: RawProgram = compiler(*derivatives, circuit=circuit)
|
|
80
77
|
else:
|
|
81
|
-
raw_program: RawProgram =
|
|
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.
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"/O2"
|
|
14
14
|
],
|
|
15
15
|
"include_dirs": [
|
|
16
|
-
"C:\\Users\\runneradmin\\AppData\\Local\\Temp\\build-env-
|
|
16
|
+
"C:\\Users\\runneradmin\\AppData\\Local\\Temp\\build-env-ca0w3uvm\\Lib\\site-packages\\numpy\\_core\\include"
|
|
17
17
|
],
|
|
18
18
|
"name": "ck.pgm_compiler.support.circuit_table._circuit_table_cy",
|
|
19
19
|
"sources": [
|
|
Binary file
|
|
@@ -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
|
-
|
|
312
|
-
|
|
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
|
|
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:
|
|
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,
|
|
47
|
+
def __call__(self, var_values: NDArrayNumeric | Sequence[int | float] | int | float) -> NDArrayNumeric:
|
|
45
48
|
"""
|
|
46
|
-
Call the raw program as a function
|
|
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
|
-
|
|
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(
|
|
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
|
|
33
|
-
_MAX_STATES_16: int = 2 ** 16
|
|
34
|
-
_MAX_STATES_32: int = 2 ** 32
|
|
35
|
-
_MAX_STATES_64: int = 2 ** 64
|
|
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
|
|
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.
|
|
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
|