compiled-knowledge 4.0.0a20__cp313-cp313-musllinux_1_2_x86_64.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/__init__.py +0 -0
- ck/circuit/__init__.py +17 -0
- ck/circuit/_circuit_cy.c +37520 -0
- ck/circuit/_circuit_cy.cpython-313-x86_64-linux-musl.so +0 -0
- ck/circuit/_circuit_cy.pxd +32 -0
- ck/circuit/_circuit_cy.pyx +768 -0
- ck/circuit/_circuit_py.py +836 -0
- ck/circuit/tmp_const.py +74 -0
- ck/circuit_compiler/__init__.py +2 -0
- ck/circuit_compiler/circuit_compiler.py +26 -0
- ck/circuit_compiler/cython_vm_compiler/__init__.py +1 -0
- ck/circuit_compiler/cython_vm_compiler/_compiler.c +19821 -0
- ck/circuit_compiler/cython_vm_compiler/_compiler.cpython-313-x86_64-linux-musl.so +0 -0
- ck/circuit_compiler/cython_vm_compiler/_compiler.pyx +380 -0
- ck/circuit_compiler/cython_vm_compiler/cython_vm_compiler.py +121 -0
- ck/circuit_compiler/interpret_compiler.py +223 -0
- ck/circuit_compiler/llvm_compiler.py +388 -0
- ck/circuit_compiler/llvm_vm_compiler.py +546 -0
- ck/circuit_compiler/named_circuit_compilers.py +57 -0
- ck/circuit_compiler/support/__init__.py +0 -0
- ck/circuit_compiler/support/circuit_analyser/__init__.py +13 -0
- ck/circuit_compiler/support/circuit_analyser/_circuit_analyser_cy.c +10615 -0
- ck/circuit_compiler/support/circuit_analyser/_circuit_analyser_cy.cpython-313-x86_64-linux-musl.so +0 -0
- ck/circuit_compiler/support/circuit_analyser/_circuit_analyser_cy.pyx +98 -0
- ck/circuit_compiler/support/circuit_analyser/_circuit_analyser_py.py +93 -0
- ck/circuit_compiler/support/input_vars.py +148 -0
- ck/circuit_compiler/support/llvm_ir_function.py +234 -0
- ck/example/__init__.py +53 -0
- ck/example/alarm.py +366 -0
- ck/example/asia.py +28 -0
- ck/example/binary_clique.py +32 -0
- ck/example/bow_tie.py +33 -0
- ck/example/cancer.py +37 -0
- ck/example/chain.py +38 -0
- ck/example/child.py +199 -0
- ck/example/clique.py +33 -0
- ck/example/cnf_pgm.py +39 -0
- ck/example/diamond_square.py +68 -0
- ck/example/earthquake.py +36 -0
- ck/example/empty.py +10 -0
- ck/example/hailfinder.py +539 -0
- ck/example/hepar2.py +628 -0
- ck/example/insurance.py +504 -0
- ck/example/loop.py +40 -0
- ck/example/mildew.py +38161 -0
- ck/example/munin.py +22982 -0
- ck/example/pathfinder.py +53747 -0
- ck/example/rain.py +39 -0
- ck/example/rectangle.py +161 -0
- ck/example/run.py +30 -0
- ck/example/sachs.py +129 -0
- ck/example/sprinkler.py +30 -0
- ck/example/star.py +44 -0
- ck/example/stress.py +64 -0
- ck/example/student.py +43 -0
- ck/example/survey.py +46 -0
- ck/example/triangle_square.py +54 -0
- ck/example/truss.py +49 -0
- ck/in_out/__init__.py +3 -0
- ck/in_out/parse_ace_lmap.py +216 -0
- ck/in_out/parse_ace_nnf.py +322 -0
- ck/in_out/parse_net.py +480 -0
- ck/in_out/parser_utils.py +185 -0
- ck/in_out/pgm_pickle.py +42 -0
- ck/in_out/pgm_python.py +268 -0
- ck/in_out/render_bugs.py +111 -0
- ck/in_out/render_net.py +177 -0
- ck/in_out/render_pomegranate.py +184 -0
- ck/pgm.py +3475 -0
- ck/pgm_circuit/__init__.py +1 -0
- ck/pgm_circuit/marginals_program.py +352 -0
- ck/pgm_circuit/mpe_program.py +237 -0
- ck/pgm_circuit/pgm_circuit.py +79 -0
- ck/pgm_circuit/program_with_slotmap.py +236 -0
- ck/pgm_circuit/slot_map.py +35 -0
- ck/pgm_circuit/support/__init__.py +0 -0
- ck/pgm_circuit/support/compile_circuit.py +83 -0
- ck/pgm_circuit/target_marginals_program.py +103 -0
- ck/pgm_circuit/wmc_program.py +323 -0
- ck/pgm_compiler/__init__.py +2 -0
- ck/pgm_compiler/ace/__init__.py +1 -0
- ck/pgm_compiler/ace/ace.py +299 -0
- ck/pgm_compiler/factor_elimination.py +395 -0
- ck/pgm_compiler/named_pgm_compilers.py +63 -0
- ck/pgm_compiler/pgm_compiler.py +19 -0
- ck/pgm_compiler/recursive_conditioning.py +231 -0
- ck/pgm_compiler/support/__init__.py +0 -0
- ck/pgm_compiler/support/circuit_table/__init__.py +17 -0
- ck/pgm_compiler/support/circuit_table/_circuit_table_cy.c +16393 -0
- ck/pgm_compiler/support/circuit_table/_circuit_table_cy.cpython-313-x86_64-linux-musl.so +0 -0
- ck/pgm_compiler/support/circuit_table/_circuit_table_cy.pyx +332 -0
- ck/pgm_compiler/support/circuit_table/_circuit_table_py.py +304 -0
- ck/pgm_compiler/support/clusters.py +568 -0
- ck/pgm_compiler/support/factor_tables.py +406 -0
- ck/pgm_compiler/support/join_tree.py +332 -0
- ck/pgm_compiler/support/named_compiler_maker.py +43 -0
- ck/pgm_compiler/variable_elimination.py +91 -0
- ck/probability/__init__.py +0 -0
- ck/probability/empirical_probability_space.py +50 -0
- ck/probability/pgm_probability_space.py +32 -0
- ck/probability/probability_space.py +622 -0
- ck/program/__init__.py +3 -0
- ck/program/program.py +137 -0
- ck/program/program_buffer.py +180 -0
- ck/program/raw_program.py +67 -0
- ck/sampling/__init__.py +0 -0
- ck/sampling/forward_sampler.py +211 -0
- ck/sampling/marginals_direct_sampler.py +113 -0
- ck/sampling/sampler.py +62 -0
- ck/sampling/sampler_support.py +232 -0
- ck/sampling/uniform_sampler.py +72 -0
- ck/sampling/wmc_direct_sampler.py +171 -0
- ck/sampling/wmc_gibbs_sampler.py +153 -0
- ck/sampling/wmc_metropolis_sampler.py +165 -0
- ck/sampling/wmc_rejection_sampler.py +115 -0
- ck/utils/__init__.py +0 -0
- ck/utils/iter_extras.py +163 -0
- ck/utils/local_config.py +270 -0
- ck/utils/map_list.py +128 -0
- ck/utils/map_set.py +128 -0
- ck/utils/np_extras.py +51 -0
- ck/utils/random_extras.py +64 -0
- ck/utils/tmp_dir.py +94 -0
- ck_demos/__init__.py +0 -0
- ck_demos/ace/__init__.py +0 -0
- ck_demos/ace/copy_ace_to_ck.py +15 -0
- ck_demos/ace/demo_ace.py +49 -0
- ck_demos/all_demos.py +88 -0
- ck_demos/circuit/__init__.py +0 -0
- ck_demos/circuit/demo_circuit_dump.py +22 -0
- ck_demos/circuit/demo_derivatives.py +43 -0
- ck_demos/circuit_compiler/__init__.py +0 -0
- ck_demos/circuit_compiler/compare_circuit_compilers.py +32 -0
- ck_demos/circuit_compiler/show_llvm_program.py +26 -0
- ck_demos/pgm/__init__.py +0 -0
- ck_demos/pgm/demo_pgm_dump.py +18 -0
- ck_demos/pgm/demo_pgm_dump_stress.py +18 -0
- ck_demos/pgm/demo_pgm_string_rendering.py +15 -0
- ck_demos/pgm/show_examples.py +25 -0
- ck_demos/pgm_compiler/__init__.py +0 -0
- ck_demos/pgm_compiler/compare_pgm_compilers.py +63 -0
- ck_demos/pgm_compiler/demo_compiler_dump.py +60 -0
- ck_demos/pgm_compiler/demo_factor_elimination.py +47 -0
- ck_demos/pgm_compiler/demo_join_tree.py +25 -0
- ck_demos/pgm_compiler/demo_marginals_program.py +53 -0
- ck_demos/pgm_compiler/demo_mpe_program.py +55 -0
- ck_demos/pgm_compiler/demo_pgm_compiler.py +38 -0
- ck_demos/pgm_compiler/demo_recursive_conditioning.py +33 -0
- ck_demos/pgm_compiler/demo_variable_elimination.py +33 -0
- ck_demos/pgm_compiler/demo_wmc_program.py +29 -0
- ck_demos/pgm_compiler/time_fe_compiler.py +93 -0
- ck_demos/pgm_inference/__init__.py +0 -0
- ck_demos/pgm_inference/demo_inferencing_basic.py +188 -0
- ck_demos/pgm_inference/demo_inferencing_mpe_cancer.py +45 -0
- ck_demos/pgm_inference/demo_inferencing_wmc_and_mpe_sprinkler.py +154 -0
- ck_demos/pgm_inference/demo_inferencing_wmc_student.py +110 -0
- ck_demos/programs/__init__.py +0 -0
- ck_demos/programs/demo_program_buffer.py +24 -0
- ck_demos/programs/demo_program_multi.py +24 -0
- ck_demos/programs/demo_program_none.py +19 -0
- ck_demos/programs/demo_program_single.py +23 -0
- ck_demos/programs/demo_raw_program_interpreted.py +21 -0
- ck_demos/programs/demo_raw_program_llvm.py +21 -0
- ck_demos/sampling/__init__.py +0 -0
- ck_demos/sampling/check_sampler.py +71 -0
- ck_demos/sampling/demo_marginal_direct_sampler.py +40 -0
- ck_demos/sampling/demo_uniform_sampler.py +38 -0
- ck_demos/sampling/demo_wmc_direct_sampler.py +40 -0
- ck_demos/utils/__init__.py +0 -0
- ck_demos/utils/compare.py +120 -0
- ck_demos/utils/convert_network.py +45 -0
- ck_demos/utils/sample_model.py +216 -0
- ck_demos/utils/stop_watch.py +384 -0
- compiled_knowledge-4.0.0a20.dist-info/METADATA +50 -0
- compiled_knowledge-4.0.0a20.dist-info/RECORD +178 -0
- compiled_knowledge-4.0.0a20.dist-info/WHEEL +5 -0
- compiled_knowledge-4.0.0a20.dist-info/licenses/LICENSE.txt +21 -0
- compiled_knowledge-4.0.0a20.dist-info/top_level.txt +2 -0
ck/utils/local_config.py
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides access to local configuration variables.
|
|
3
|
+
|
|
4
|
+
Local configuration variables are {variable} = {value} pairs that
|
|
5
|
+
are defined externally to CK for the purposes of adapting
|
|
6
|
+
to the local environment that CK is installed in. Local
|
|
7
|
+
configuration variables are not expected to modify the
|
|
8
|
+
behaviour of algorithms implemented in CK.
|
|
9
|
+
|
|
10
|
+
The primary method to access local configuration is `get`. Various
|
|
11
|
+
other getter methods wrap `get`.
|
|
12
|
+
|
|
13
|
+
The `get` method will search for a value for a requested variable
|
|
14
|
+
using the following steps.
|
|
15
|
+
1) Check the `programmatic config` which is a dictionary that
|
|
16
|
+
can be directly updated.
|
|
17
|
+
2) Check the PYTHONPATH for a module called `config` (i.e., a
|
|
18
|
+
`config.py` file) for global variables defined in that module.
|
|
19
|
+
3) Check the system environment variables (`os.environ`).
|
|
20
|
+
|
|
21
|
+
Variable names must be a valid Python identifier. Only valid
|
|
22
|
+
value types are supported, as per the function `valid_value`.
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
from ck.utils.local_config import config
|
|
26
|
+
|
|
27
|
+
# assume `config.py` is in the PYTHONPATH and contains:
|
|
28
|
+
# ABC = 123
|
|
29
|
+
# DEF = 456
|
|
30
|
+
|
|
31
|
+
val = config.ABC # val = 123
|
|
32
|
+
val = config.XYZ # will raise an exception
|
|
33
|
+
val = config.get('ABC') # val = 123
|
|
34
|
+
val = config['DEF'] # val = 456
|
|
35
|
+
val = config['XYZ'] # will raise an exception
|
|
36
|
+
val = config.get('XYZ') # val = None
|
|
37
|
+
val = config.get('XYZ', 999) # val = 999
|
|
38
|
+
|
|
39
|
+
from ck.utils.local_config import get_params
|
|
40
|
+
|
|
41
|
+
val = get_params('ABC') # val = ('ABC', 123)
|
|
42
|
+
val = get_params('ABC', 'DEF') # val = (('ABC', 123), ('DEF', 456))
|
|
43
|
+
val = get_params('ABC', 'DEF', sep='=') # val = ('ABC=123', 'DEF=456')
|
|
44
|
+
val = get_params('ABC;DEF', delim=';') # val = 'ABC=123;DEF=456'
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
import inspect
|
|
49
|
+
import os
|
|
50
|
+
from ast import literal_eval
|
|
51
|
+
from itertools import chain
|
|
52
|
+
from typing import Optional, Dict, Any, Sequence, Iterable
|
|
53
|
+
|
|
54
|
+
from ck.utils.iter_extras import flatten
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
# Try to import the user's `config.py`
|
|
58
|
+
import config as _user_config
|
|
59
|
+
except ImportError:
|
|
60
|
+
_user_config = None
|
|
61
|
+
|
|
62
|
+
# Sentinel object
|
|
63
|
+
_NIL = object()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Config:
|
|
67
|
+
|
|
68
|
+
def __init__(self):
|
|
69
|
+
self._programmatic_config: Dict[str, Any] = {}
|
|
70
|
+
|
|
71
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
72
|
+
"""
|
|
73
|
+
Get the value of the given local configuration variable.
|
|
74
|
+
If the configuration variable is not available, return the given default value.
|
|
75
|
+
"""
|
|
76
|
+
if not key.isidentifier():
|
|
77
|
+
raise KeyError(f'invalid local configuration parameter: {key!r}')
|
|
78
|
+
|
|
79
|
+
# Check the programmatic config
|
|
80
|
+
value = self._programmatic_config.get(key, _NIL)
|
|
81
|
+
if value is not _NIL:
|
|
82
|
+
return value
|
|
83
|
+
|
|
84
|
+
# Check config.py
|
|
85
|
+
if _user_config is not None:
|
|
86
|
+
value = vars(_user_config).get(key, _NIL)
|
|
87
|
+
if value is not _NIL:
|
|
88
|
+
if not valid_value(value):
|
|
89
|
+
raise KeyError(f'user configuration file contains an invalid value for variable: {key!r}')
|
|
90
|
+
return value
|
|
91
|
+
|
|
92
|
+
# Check the OS environment
|
|
93
|
+
value = os.environ.get(key, _NIL)
|
|
94
|
+
if value is not _NIL:
|
|
95
|
+
return value
|
|
96
|
+
|
|
97
|
+
# Not found - return the default value
|
|
98
|
+
return default
|
|
99
|
+
|
|
100
|
+
def __contains__(self, key: str) -> bool:
|
|
101
|
+
return self.get(key, _NIL) is not _NIL
|
|
102
|
+
|
|
103
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
104
|
+
"""
|
|
105
|
+
Programmatically overwrite a local configuration variable.
|
|
106
|
+
"""
|
|
107
|
+
if not key.isidentifier():
|
|
108
|
+
raise KeyError(f'invalid local configuration parameter: {key!r}')
|
|
109
|
+
if not valid_value(value):
|
|
110
|
+
raise ValueError(f'invalid local configuration parameter value: {value!r}')
|
|
111
|
+
self._programmatic_config[key] = value
|
|
112
|
+
|
|
113
|
+
def __getitem__(self, key: str):
|
|
114
|
+
"""
|
|
115
|
+
Get the value of the given configuration variable.
|
|
116
|
+
If the configuration variable is not available, raise a KeyError.
|
|
117
|
+
"""
|
|
118
|
+
value = self.get(key, _NIL)
|
|
119
|
+
if value is _NIL:
|
|
120
|
+
raise KeyError(f'undefined local configuration parameter: {key}')
|
|
121
|
+
return value
|
|
122
|
+
|
|
123
|
+
def __getattr__(self, key: str):
|
|
124
|
+
"""
|
|
125
|
+
Get the value of the given configuration variable.
|
|
126
|
+
If the configuration variable is not available, raise a KeyError.
|
|
127
|
+
"""
|
|
128
|
+
value = self.get(key, _NIL)
|
|
129
|
+
if value is _NIL:
|
|
130
|
+
raise KeyError(f'undefined local configuration parameter: {key}')
|
|
131
|
+
return value
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# The global local config object.
|
|
135
|
+
config = Config()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def valid_value(value: Any) -> bool:
|
|
139
|
+
"""
|
|
140
|
+
Does the given value have an acceptable type for
|
|
141
|
+
a configuration variable?
|
|
142
|
+
"""
|
|
143
|
+
if isinstance(value, (list, tuple, set)):
|
|
144
|
+
return all(valid_value(elem) for elem in value)
|
|
145
|
+
if isinstance(value, dict):
|
|
146
|
+
return all(valid_value(elem) for elem in chain(value.keys(), value.values()))
|
|
147
|
+
if callable(value) or inspect.isfunction(value) or inspect.ismodule(value):
|
|
148
|
+
return False
|
|
149
|
+
# All tests pass
|
|
150
|
+
return True
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# noinspection PyShadowingNames
|
|
154
|
+
def get_params(
|
|
155
|
+
*keys: str,
|
|
156
|
+
sep: Optional[str] = None,
|
|
157
|
+
delim: Optional[str] = None,
|
|
158
|
+
config: Config = config,
|
|
159
|
+
):
|
|
160
|
+
"""
|
|
161
|
+
Return one or more configuration parameter as key-value pairs.
|
|
162
|
+
|
|
163
|
+
If `sep` is None then each key-value pair is returned as a tuple, otherwise
|
|
164
|
+
each key-value pair is returned as a string with `sep` as the separator.
|
|
165
|
+
|
|
166
|
+
If `delim` is None then each key is treated as is. If one key is provided then
|
|
167
|
+
its value is returned. If multiple keys are provided, then multiple values
|
|
168
|
+
are returned in a tuple.
|
|
169
|
+
|
|
170
|
+
If `delim` is not None, then keys are split using `delim`, and results
|
|
171
|
+
are returned as a single string with `delim` as the delimiter. If
|
|
172
|
+
`delim` is not None then the default value for `sep` is '='.
|
|
173
|
+
|
|
174
|
+
For example, assume config.py contains: ABC = 123 and DEF = 456,
|
|
175
|
+
then:
|
|
176
|
+
get_params('ABC') -> ('ABC', 123)
|
|
177
|
+
get_params('ABC', 'DEF') -> ('ABC', 123), ('DEF', 456)
|
|
178
|
+
get_params('ABC', sep='=') = 'ABC=123'
|
|
179
|
+
get_params('ABC', 'DEF', sep='=') = 'ABC=123', 'DEF=456'
|
|
180
|
+
get_params('ABC;DEF', delim=';') = 'ABC=123;DEF=456'
|
|
181
|
+
get_params('ABC;DEF', sep='==', delim=';') = 'ABC==123;DEF==456'
|
|
182
|
+
|
|
183
|
+
:param keys: the names of variables to access.
|
|
184
|
+
:param sep: the separator character between {variable} and {value}.
|
|
185
|
+
:param delim: the delimiter character between key-value pairs.
|
|
186
|
+
:param config: a Config instance to update. Default is the global config.
|
|
187
|
+
"""
|
|
188
|
+
if delim is not None:
|
|
189
|
+
keys = flatten(key.split(delim) for key in keys)
|
|
190
|
+
if sep is None:
|
|
191
|
+
sep = '='
|
|
192
|
+
|
|
193
|
+
if sep is None:
|
|
194
|
+
items = ((key, config[key]) for key in keys)
|
|
195
|
+
else:
|
|
196
|
+
items = (f'{key}{sep}{config[key]!r}' for key in keys)
|
|
197
|
+
|
|
198
|
+
if delim is None:
|
|
199
|
+
result = tuple(items)
|
|
200
|
+
if len(result) == 1:
|
|
201
|
+
result = result[0]
|
|
202
|
+
else:
|
|
203
|
+
result = delim.join(str(item) for item in items)
|
|
204
|
+
|
|
205
|
+
return result
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# noinspection PyShadowingNames
|
|
209
|
+
def update_config(
|
|
210
|
+
argv: Sequence[str],
|
|
211
|
+
valid_keys: Optional[Iterable[str]] = None,
|
|
212
|
+
*,
|
|
213
|
+
sep: str = '=',
|
|
214
|
+
strip_whitespace: bool = True,
|
|
215
|
+
config: Config = config,
|
|
216
|
+
) -> None:
|
|
217
|
+
"""
|
|
218
|
+
Programmatically overwrite a local configuration variable from a command line `argv`.
|
|
219
|
+
|
|
220
|
+
Variable values are interpreted as per a Python literal.
|
|
221
|
+
|
|
222
|
+
Example usage:
|
|
223
|
+
import sys
|
|
224
|
+
from ck.utils.local_config import update_config
|
|
225
|
+
|
|
226
|
+
def main():
|
|
227
|
+
...
|
|
228
|
+
|
|
229
|
+
if __name__ == '__main__':
|
|
230
|
+
update_config(sys.argv, ['in_name', 'out_name'])
|
|
231
|
+
main()
|
|
232
|
+
|
|
233
|
+
:param argv: a collection of strings in the form '{variable}={value}'.
|
|
234
|
+
Variables not in `valid_keys` will raise a ValueError.
|
|
235
|
+
:param valid_keys: an optional collection of strings that are valid variables to
|
|
236
|
+
process from argv, or None to accept all variables.
|
|
237
|
+
:param sep: the separator character between {variable} and {value}.
|
|
238
|
+
Defaults is '='.
|
|
239
|
+
:param strip_whitespace: If True, then whitespace is stripped from
|
|
240
|
+
the value before updating the config. Whitespace is always stripped
|
|
241
|
+
from the variable name.
|
|
242
|
+
:param config: a Config instance to update. Default is the global config.
|
|
243
|
+
"""
|
|
244
|
+
if valid_keys is not None:
|
|
245
|
+
valid_keys = set(valid_keys)
|
|
246
|
+
|
|
247
|
+
for arg in argv:
|
|
248
|
+
var_val = str(arg).split(sep, maxsplit=1)
|
|
249
|
+
if len(var_val) != 2:
|
|
250
|
+
raise ValueError(f'cannot split argument: {arg!r} using separator {sep!r}')
|
|
251
|
+
|
|
252
|
+
var, val = var_val
|
|
253
|
+
var = var.strip()
|
|
254
|
+
if strip_whitespace:
|
|
255
|
+
val = val.strip()
|
|
256
|
+
|
|
257
|
+
if valid_keys is not None and var not in valid_keys:
|
|
258
|
+
raise KeyError(f'invalid key: {arg!r}')
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
interpreted = literal_eval(val)
|
|
262
|
+
except (ValueError, SyntaxError) as err:
|
|
263
|
+
# Some operating systems strip quotes off
|
|
264
|
+
# strings, so we try to recover.
|
|
265
|
+
if '"' in val or "'" in val:
|
|
266
|
+
# Too hard... forget it.
|
|
267
|
+
raise err
|
|
268
|
+
interpreted = str(val)
|
|
269
|
+
|
|
270
|
+
config[var] = interpreted
|
ck/utils/map_list.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines a class "MapList" for mapping keys to lists.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import TypeVar, Generic, List, Dict, MutableMapping, KeysView, ValuesView, ItemsView, Iterable, Iterator
|
|
7
|
+
|
|
8
|
+
_K = TypeVar('_K')
|
|
9
|
+
_V = TypeVar('_V')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MapList(Generic[_K, _V], MutableMapping[_K, List[_V]]):
|
|
13
|
+
"""
|
|
14
|
+
A MapList keeps a list for each key, unlike a dict which keeps only
|
|
15
|
+
a single element for each key.
|
|
16
|
+
"""
|
|
17
|
+
__slots__ = ('_map', )
|
|
18
|
+
|
|
19
|
+
def __init__(self, *args, **kwargs):
|
|
20
|
+
self._map: Dict[_K, List[_V]] = {}
|
|
21
|
+
self.update(*args, extend=False, **kwargs)
|
|
22
|
+
|
|
23
|
+
def __str__(self) -> str:
|
|
24
|
+
return str(self._map)
|
|
25
|
+
|
|
26
|
+
def __repr__(self) -> str:
|
|
27
|
+
args = ', '.join(f'{key!r}:{val!r}' for key, val in self.items())
|
|
28
|
+
class_name = self.__class__.__name__
|
|
29
|
+
return f'{class_name}({args})'
|
|
30
|
+
|
|
31
|
+
def __len__(self) -> int:
|
|
32
|
+
return len(self._map)
|
|
33
|
+
|
|
34
|
+
def __bool__(self) -> bool:
|
|
35
|
+
return len(self) > 0
|
|
36
|
+
|
|
37
|
+
def __getitem__(self, key) -> List[_V]:
|
|
38
|
+
return self._map[key]
|
|
39
|
+
|
|
40
|
+
def __setitem__(self, key: _K, val: List[_V]):
|
|
41
|
+
if not isinstance(val, list):
|
|
42
|
+
class_name = self.__class__.__name__
|
|
43
|
+
raise RuntimeError(f'every {class_name} value must be a list')
|
|
44
|
+
self._map[key] = val
|
|
45
|
+
|
|
46
|
+
def __delitem__(self, key: _K):
|
|
47
|
+
del self._map[key]
|
|
48
|
+
|
|
49
|
+
def __iter__(self) -> Iterator[_K]:
|
|
50
|
+
return iter(self._map)
|
|
51
|
+
|
|
52
|
+
def __contains__(self, key: _K) -> bool:
|
|
53
|
+
return key in self._map
|
|
54
|
+
|
|
55
|
+
def update(self, *args, extend=False, **kwargs):
|
|
56
|
+
k: _K
|
|
57
|
+
v: List[_V]
|
|
58
|
+
if extend:
|
|
59
|
+
for k, v in dict(*args, **kwargs).items():
|
|
60
|
+
self.extend(k, v)
|
|
61
|
+
else:
|
|
62
|
+
for k, v in dict(*args, **kwargs).items():
|
|
63
|
+
self[k] = v
|
|
64
|
+
|
|
65
|
+
def keys(self) -> KeysView[_K]:
|
|
66
|
+
return self._map.keys()
|
|
67
|
+
|
|
68
|
+
def values(self) -> ValuesView[List[_V]]:
|
|
69
|
+
return self._map.values()
|
|
70
|
+
|
|
71
|
+
def items(self) -> ItemsView[_K, List[_V]]:
|
|
72
|
+
return self._map.items()
|
|
73
|
+
|
|
74
|
+
def get(self, key: _K, default=None):
|
|
75
|
+
"""
|
|
76
|
+
Get the list corresponding to the given key.
|
|
77
|
+
If the key is not yet in the MapList then the
|
|
78
|
+
supplied default will be returned.
|
|
79
|
+
"""
|
|
80
|
+
return self._map.get(key, default)
|
|
81
|
+
|
|
82
|
+
def get_list(self, key: _K) -> List[_V]:
|
|
83
|
+
"""
|
|
84
|
+
Get the list corresponding to the given key.
|
|
85
|
+
|
|
86
|
+
This method will always return a list in the MapList, even if
|
|
87
|
+
it requires a new list being created.
|
|
88
|
+
|
|
89
|
+
Modifying the returned list affects this MapList object.
|
|
90
|
+
"""
|
|
91
|
+
the_list = self._map.get(key)
|
|
92
|
+
if the_list is None:
|
|
93
|
+
the_list = []
|
|
94
|
+
self._map[key] = the_list
|
|
95
|
+
return the_list
|
|
96
|
+
|
|
97
|
+
def append(self, key: _K, item: _V):
|
|
98
|
+
"""
|
|
99
|
+
Append the given item to the list identified by the given key.
|
|
100
|
+
"""
|
|
101
|
+
self.get_list(key).append(item)
|
|
102
|
+
|
|
103
|
+
def extend(self, key: _K, items: Iterable[_V]):
|
|
104
|
+
"""
|
|
105
|
+
Extend the given item to the list identified by the given key.
|
|
106
|
+
"""
|
|
107
|
+
return self.get_list(key).extend(items)
|
|
108
|
+
|
|
109
|
+
def extend_map_list(self, map_list: MapList[_K, _V]):
|
|
110
|
+
"""
|
|
111
|
+
Add all the keyed given items to the list identified by each key.
|
|
112
|
+
"""
|
|
113
|
+
for key, items in map_list.items():
|
|
114
|
+
self.extend(key, items)
|
|
115
|
+
|
|
116
|
+
def clear(self):
|
|
117
|
+
"""
|
|
118
|
+
Remove all items.
|
|
119
|
+
"""
|
|
120
|
+
return self._map.clear()
|
|
121
|
+
|
|
122
|
+
def clear_empty(self):
|
|
123
|
+
"""
|
|
124
|
+
Remove all empty values.
|
|
125
|
+
"""
|
|
126
|
+
keys_to_remove = [key for key, value in self._map.items() if len(value) == 0]
|
|
127
|
+
for key in keys_to_remove:
|
|
128
|
+
del self._map[key]
|
ck/utils/map_set.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines a class "MapSet" for mapping keys to sets.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import TypeVar, Generic, Set, Dict, MutableMapping, Iterator, KeysView, ValuesView, ItemsView, Iterable
|
|
7
|
+
|
|
8
|
+
_K = TypeVar('_K')
|
|
9
|
+
_V = TypeVar('_V')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MapSet(Generic[_K, _V], MutableMapping[_K, Set[_V]]):
|
|
13
|
+
"""
|
|
14
|
+
A MapSet keeps a set for each key, unlike a dict which keeps only
|
|
15
|
+
a single element for each key.
|
|
16
|
+
"""
|
|
17
|
+
__slots__ = ('_map',)
|
|
18
|
+
|
|
19
|
+
def __init__(self, *args, **kwargs):
|
|
20
|
+
self._map: Dict[_K, Set[_V]] = {}
|
|
21
|
+
self.update(*args, add_all=False, **kwargs)
|
|
22
|
+
|
|
23
|
+
def __str__(self) -> str:
|
|
24
|
+
return str(self._map)
|
|
25
|
+
|
|
26
|
+
def __repr__(self) -> str:
|
|
27
|
+
args = ', '.join(f'{key!r}:{val!r}' for key, val in self.items())
|
|
28
|
+
class_name = self.__class__.__name__
|
|
29
|
+
return f'{class_name}({args})'
|
|
30
|
+
|
|
31
|
+
def __len__(self) -> int:
|
|
32
|
+
return len(self._map)
|
|
33
|
+
|
|
34
|
+
def __bool__(self) -> bool:
|
|
35
|
+
return len(self) > 0
|
|
36
|
+
|
|
37
|
+
def __getitem__(self, key: _K) -> Set[_V]:
|
|
38
|
+
return self._map[key]
|
|
39
|
+
|
|
40
|
+
def __setitem__(self, key: _K, val: Set[_V]):
|
|
41
|
+
if not isinstance(val, set):
|
|
42
|
+
class_name = self.__class__.__name__
|
|
43
|
+
raise RuntimeError(f'every {class_name} value must be a set')
|
|
44
|
+
self._map[key] = val
|
|
45
|
+
|
|
46
|
+
def __delitem__(self, key: _K):
|
|
47
|
+
del self._map[key]
|
|
48
|
+
|
|
49
|
+
def __iter__(self) -> Iterator[_K]:
|
|
50
|
+
return iter(self._map)
|
|
51
|
+
|
|
52
|
+
def __contains__(self, key: _K) -> bool:
|
|
53
|
+
return key in self._map
|
|
54
|
+
|
|
55
|
+
def update(self, *args, add_all=True, **kwargs):
|
|
56
|
+
k: _K
|
|
57
|
+
v: Set[_V]
|
|
58
|
+
if add_all:
|
|
59
|
+
for k, v in dict(*args, **kwargs).items():
|
|
60
|
+
self.add_all(k, v)
|
|
61
|
+
else:
|
|
62
|
+
for k, v in dict(*args, **kwargs).items():
|
|
63
|
+
self[k] = v
|
|
64
|
+
|
|
65
|
+
def keys(self) -> KeysView[_K]:
|
|
66
|
+
return self._map.keys()
|
|
67
|
+
|
|
68
|
+
def values(self) -> ValuesView[Set[_V]]:
|
|
69
|
+
return self._map.values()
|
|
70
|
+
|
|
71
|
+
def items(self) -> ItemsView[_K, Set[_V]]:
|
|
72
|
+
return self._map.items()
|
|
73
|
+
|
|
74
|
+
def get(self, key: _K, default=None):
|
|
75
|
+
"""
|
|
76
|
+
Get the set corresponding to the given key.
|
|
77
|
+
If the key is not yet in the MapSet then the
|
|
78
|
+
supplied default will be returned.
|
|
79
|
+
"""
|
|
80
|
+
return self._map.get(key, default)
|
|
81
|
+
|
|
82
|
+
def get_set(self, key: _K) -> Set[_V]:
|
|
83
|
+
"""
|
|
84
|
+
Get the set corresponding to the given key.
|
|
85
|
+
|
|
86
|
+
This method will always return a set in the MapSet, even if
|
|
87
|
+
it requires a new set being created.
|
|
88
|
+
|
|
89
|
+
Modifying the returned set affects this MapSet object.
|
|
90
|
+
"""
|
|
91
|
+
the_set = self._map.get(key)
|
|
92
|
+
if the_set is None:
|
|
93
|
+
the_set = set()
|
|
94
|
+
self._map[key] = the_set
|
|
95
|
+
return the_set
|
|
96
|
+
|
|
97
|
+
def add(self, key: _K, item: _V):
|
|
98
|
+
"""
|
|
99
|
+
Add the given item to the set identified by the given key.
|
|
100
|
+
"""
|
|
101
|
+
self.get_set(key).add(item)
|
|
102
|
+
|
|
103
|
+
def add_all(self, key: _K, items: Iterable[_V]):
|
|
104
|
+
"""
|
|
105
|
+
Add all the given items to the set identified by the given key.
|
|
106
|
+
"""
|
|
107
|
+
return self.get_set(key).update(items)
|
|
108
|
+
|
|
109
|
+
def add_map_set(self, map_set: MapSet[_K, _V]):
|
|
110
|
+
"""
|
|
111
|
+
Add all the keyed given items to the set identified by each key.
|
|
112
|
+
"""
|
|
113
|
+
for key, items in map_set.items():
|
|
114
|
+
self.add_all(key, items)
|
|
115
|
+
|
|
116
|
+
def clear(self):
|
|
117
|
+
"""
|
|
118
|
+
Remove all items.
|
|
119
|
+
"""
|
|
120
|
+
return self._map.clear()
|
|
121
|
+
|
|
122
|
+
def clear_empty(self):
|
|
123
|
+
"""
|
|
124
|
+
Remove all empty values.
|
|
125
|
+
"""
|
|
126
|
+
keys_to_remove = [key for key, value in self._map.items() if len(value) == 0]
|
|
127
|
+
for key in keys_to_remove:
|
|
128
|
+
del self._map[key]
|
ck/utils/np_extras.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Type used by numpy and ctypes for data type definition
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
# A numpy/ctypes data type.
|
|
6
|
+
DType = np.typing.DTypeLike
|
|
7
|
+
|
|
8
|
+
# A numpy data type for holding state indexes.
|
|
9
|
+
DTypeStates = np.dtypes.UInt8DType | np.dtypes.UInt16DType | np.dtypes.UInt32DType | np.dtypes.UInt64DType
|
|
10
|
+
|
|
11
|
+
DTypeNumeric = (
|
|
12
|
+
np.dtypes.Float64DType | np.dtypes.Float32DType
|
|
13
|
+
| np.dtypes.Int8DType | np.dtypes.Int16DType | np.dtypes.Int32DType | np.dtypes.Int64DType
|
|
14
|
+
| np.dtypes.UInt8DType | np.dtypes.UInt16DType | np.dtypes.UInt32DType | np.dtypes.UInt64DType
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# A numpy array data type.
|
|
18
|
+
NDArray = np.typing.NDArray
|
|
19
|
+
|
|
20
|
+
NDArrayUInt8 = NDArray[np.dtypes.UInt8DType]
|
|
21
|
+
NDArrayUInt16 = NDArray[np.dtypes.UInt16DType]
|
|
22
|
+
NDArrayUInt32 = NDArray[np.dtypes.UInt32DType]
|
|
23
|
+
NDArrayUInt64 = NDArray[np.dtypes.UInt64DType]
|
|
24
|
+
|
|
25
|
+
NDArrayFloat64 = NDArray[np.dtypes.Float64DType]
|
|
26
|
+
|
|
27
|
+
NDArrayStates = NDArray[DTypeStates]
|
|
28
|
+
NDArrayNumeric = NDArray[DTypeNumeric]
|
|
29
|
+
|
|
30
|
+
|
|
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
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def dtype_for_number_of_states(number_of_states: int) -> DTypeStates:
|
|
39
|
+
"""
|
|
40
|
+
Infer the numpy dtype required to store any state index of the given PGM.
|
|
41
|
+
"""
|
|
42
|
+
# Infer the needed size of
|
|
43
|
+
if number_of_states <= _MAX_STATES_8:
|
|
44
|
+
return np.uint8
|
|
45
|
+
if number_of_states <= _MAX_STATES_16:
|
|
46
|
+
return np.uint16
|
|
47
|
+
if number_of_states <= _MAX_STATES_32:
|
|
48
|
+
return np.uint32
|
|
49
|
+
if number_of_states <= _MAX_STATES_64:
|
|
50
|
+
return np.uint64
|
|
51
|
+
raise ValueError(f'cannot determine dtype for the given number of states: {number_of_states!r}')
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A module with extra randomisation functions.
|
|
3
|
+
"""
|
|
4
|
+
import random
|
|
5
|
+
from typing import Protocol, Sequence, Tuple, Any, List
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Random(Protocol):
|
|
9
|
+
"""
|
|
10
|
+
A minimum protocol for a random number generator as used by CK.
|
|
11
|
+
The usual `random` package implements this protocol.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def random(self) -> float:
|
|
15
|
+
"""
|
|
16
|
+
Returns a random float in the interval [0, 1), includes 0, excludes 1.
|
|
17
|
+
"""
|
|
18
|
+
...
|
|
19
|
+
|
|
20
|
+
def randrange(self, a: int, b: int) -> int:
|
|
21
|
+
"""
|
|
22
|
+
Returns a random integer in interval [a, b), includes `a`, excludes `b`.
|
|
23
|
+
"""
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def random_pair(size: int, rand: Random = random) -> Tuple[int, int]:
|
|
28
|
+
"""
|
|
29
|
+
Return a random pair (i, j) where:
|
|
30
|
+
0 <= i < size,
|
|
31
|
+
0 <= j < size,
|
|
32
|
+
i != j.
|
|
33
|
+
"""
|
|
34
|
+
i = rand.randrange(0, size)
|
|
35
|
+
j = (i + rand.randrange(1, size)) % size
|
|
36
|
+
return i, j
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def random_permute(items: List[Any], rand: Random = random) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Randomly permute the given items.
|
|
42
|
+
For a list of length `n`, this method calls rand.randrange(...) `n - 1` times.
|
|
43
|
+
|
|
44
|
+
There is a numpy method to do this, but it uses its own random number generator.
|
|
45
|
+
"""
|
|
46
|
+
for i in range(len(items) - 1, 0, -1): # i = n - 1 down to 1
|
|
47
|
+
j = rand.randrange(0, i + 1) # 0 <= j <= i
|
|
48
|
+
items[i], items[j] = items[j], items[i] # exchange
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def random_permutation(size: int, rand: Random = random) -> Sequence[int]:
|
|
52
|
+
"""
|
|
53
|
+
Return a random permutation of the given size.
|
|
54
|
+
|
|
55
|
+
The returned list contains each integer 0 to size - 1
|
|
56
|
+
in a random order.
|
|
57
|
+
|
|
58
|
+
This method calls rand.randrange(...) 'size' times.
|
|
59
|
+
|
|
60
|
+
There is a numpy method to do this, but it uses its own random number generator.
|
|
61
|
+
"""
|
|
62
|
+
result = list(range(size))
|
|
63
|
+
random_permute(result, rand)
|
|
64
|
+
return result
|