compiled-knowledge 4.0.0a23__cp313-cp313-win_amd64.whl → 4.0.0a24__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 +1 -1
- ck/circuit/_circuit_cy.cp313-win_amd64.pyd +0 -0
- ck/circuit_compiler/circuit_compiler.py +3 -2
- ck/circuit_compiler/cython_vm_compiler/_compiler.c +152 -152
- ck/circuit_compiler/cython_vm_compiler/_compiler.cp313-win_amd64.pyd +0 -0
- 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/pgm.py +100 -102
- ck/pgm_compiler/pgm_compiler.py +1 -1
- 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/join_tree.py +3 -3
- ck/probability/empirical_probability_space.py +4 -3
- ck/probability/pgm_probability_space.py +7 -3
- ck/probability/probability_space.py +21 -15
- ck/sampling/sampler_support.py +8 -5
- {compiled_knowledge-4.0.0a23.dist-info → compiled_knowledge-4.0.0a24.dist-info}/METADATA +1 -1
- {compiled_knowledge-4.0.0a23.dist-info → compiled_knowledge-4.0.0a24.dist-info}/RECORD +21 -21
- {compiled_knowledge-4.0.0a23.dist-info → compiled_knowledge-4.0.0a24.dist-info}/WHEEL +0 -0
- {compiled_knowledge-4.0.0a23.dist-info → compiled_knowledge-4.0.0a24.dist-info}/licenses/LICENSE.txt +0 -0
- {compiled_knowledge-4.0.0a23.dist-info → compiled_knowledge-4.0.0a24.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
@@ -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-b7kylfcw\\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
|
ck/pgm.py
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
For more documentation on this module, refer to the Jupyter notebook docs/4_PGM_advanced.ipynb.
|
|
3
|
-
"""
|
|
4
1
|
from __future__ import annotations
|
|
5
2
|
|
|
6
3
|
import math
|
|
@@ -8,7 +5,7 @@ from abc import ABC, abstractmethod
|
|
|
8
5
|
from dataclasses import dataclass
|
|
9
6
|
from itertools import repeat as _repeat
|
|
10
7
|
from typing import Sequence, Tuple, Dict, Optional, overload, Set, Iterable, List, Union, Callable, \
|
|
11
|
-
Collection, Any, Iterator
|
|
8
|
+
Collection, Any, Iterator, TypeAlias
|
|
12
9
|
|
|
13
10
|
import numpy as np
|
|
14
11
|
|
|
@@ -17,21 +14,34 @@ from ck.utils.iter_extras import (
|
|
|
17
14
|
)
|
|
18
15
|
from ck.utils.np_extras import NDArrayFloat64, NDArrayUInt8
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
State =
|
|
17
|
+
State: TypeAlias = Union[int, str, bool, float, None]
|
|
18
|
+
State.__doc__ = \
|
|
19
|
+
"""
|
|
20
|
+
The type for a possible state of a random variable.
|
|
21
|
+
"""
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
Instance: TypeAlias = Sequence[int]
|
|
24
|
+
Instance.__doc__ = \
|
|
25
|
+
"""
|
|
26
|
+
An instance (of a sequence of random variables) is a sequence of integers
|
|
27
|
+
that are state indexes, co-indexed with a known sequence of random variables.
|
|
28
|
+
"""
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
Key: TypeAlias = Union[Instance, int]
|
|
31
|
+
Key.__doc__ = \
|
|
32
|
+
"""
|
|
33
|
+
A key identifies an instance, either as an instance itself or a
|
|
34
|
+
single integer, representing an instance with one dimension.
|
|
35
|
+
"""
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
Shape: TypeAlias = Sequence[int]
|
|
38
|
+
Key.__doc__ = \
|
|
39
|
+
"""
|
|
40
|
+
The type for the "shape" of a sequence of random variables.
|
|
41
|
+
That is, the shape of (rv1, rv2, rv3) is (len(rv1), len(rv2), len(rv3)).
|
|
42
|
+
"""
|
|
33
43
|
|
|
34
|
-
|
|
44
|
+
DEFAULT_CPT_TOLERANCE: float = 0.000001 # A tolerance when checking CPT distributions sum to one (or zero).
|
|
35
45
|
|
|
36
46
|
|
|
37
47
|
class PGM:
|
|
@@ -39,11 +49,9 @@ class PGM:
|
|
|
39
49
|
A probabilistic graphical model (PGM) represents a joint probability distribution over
|
|
40
50
|
a set of random variables. Specifically, a PGM is a factor graph with discrete random variables.
|
|
41
51
|
|
|
42
|
-
Add a random variable to a PGM, pgm
|
|
43
|
-
|
|
44
|
-
Add a factor to the PGM, pgm, using `factor = pgm.new_factor(...)`.
|
|
52
|
+
Add a random variable to a PGM, `pgm`, using `rv = pgm.new_rv(...)`.
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
Add a factor to the PGM, `pgm`, using `factor = pgm.new_factor(...)`.
|
|
47
55
|
"""
|
|
48
56
|
|
|
49
57
|
def __init__(self, name: Optional[str] = None):
|
|
@@ -587,7 +595,7 @@ class PGM:
|
|
|
587
595
|
# All tests passed
|
|
588
596
|
return True
|
|
589
597
|
|
|
590
|
-
def factors_are_cpts(self, tolerance: float =
|
|
598
|
+
def factors_are_cpts(self, tolerance: float = DEFAULT_CPT_TOLERANCE) -> bool:
|
|
591
599
|
"""
|
|
592
600
|
Are all factor potential functions set with parameters values
|
|
593
601
|
conforming to Conditional Probability Tables.
|
|
@@ -603,7 +611,7 @@ class PGM:
|
|
|
603
611
|
"""
|
|
604
612
|
return all(function.is_cpt(tolerance) for function in self.functions)
|
|
605
613
|
|
|
606
|
-
def check_is_bayesian_network(self, tolerance: float =
|
|
614
|
+
def check_is_bayesian_network(self, tolerance: float = DEFAULT_CPT_TOLERANCE) -> bool:
|
|
607
615
|
"""
|
|
608
616
|
Is this PGM a Bayesian network.
|
|
609
617
|
|
|
@@ -648,7 +656,7 @@ class PGM:
|
|
|
648
656
|
This method has the same semantics as `ProbabilitySpace.wmc` without conditioning.
|
|
649
657
|
|
|
650
658
|
Warning:
|
|
651
|
-
this is potentially computationally expensive as it
|
|
659
|
+
this is potentially computationally expensive as it marginalises random
|
|
652
660
|
variables not mentioned in the given indicators.
|
|
653
661
|
|
|
654
662
|
Args:
|
|
@@ -658,29 +666,11 @@ class PGM:
|
|
|
658
666
|
the product of factors, conditioned on the given instance. This is the
|
|
659
667
|
computed value of the PGM, conditioned on the given instance.
|
|
660
668
|
"""
|
|
661
|
-
#
|
|
662
|
-
#
|
|
663
|
-
#
|
|
664
|
-
#
|
|
665
|
-
#
|
|
666
|
-
# # Collect rvs not mentioned - to marginalise
|
|
667
|
-
# for rv, rv_filter in zip(self.rvs, inst_filter):
|
|
668
|
-
# if len(rv_filter) == 0:
|
|
669
|
-
# rv_filter.update(rv.state_range())
|
|
670
|
-
#
|
|
671
|
-
# def _sum_inst(_instance: Instance) -> bool:
|
|
672
|
-
# return all(
|
|
673
|
-
# (_state in _rv_filter)
|
|
674
|
-
# for _state, _rv_filter in zip(_instance, inst_filter)
|
|
675
|
-
# )
|
|
676
|
-
#
|
|
677
|
-
# # Accumulate the result
|
|
678
|
-
# sum_value = 0
|
|
679
|
-
# for instance in self.instances():
|
|
680
|
-
# if _sum_inst(instance):
|
|
681
|
-
# sum_value += self.value_product(instance)
|
|
682
|
-
#
|
|
683
|
-
# return sum_value
|
|
669
|
+
# Rather than naively checking all possible states of the PGM random
|
|
670
|
+
# variables, this method works to define the state space that should
|
|
671
|
+
# be summed over, based on the given indicators. Thus, if the given
|
|
672
|
+
# indicators constrain the state space to a small number of possibilities,
|
|
673
|
+
# then the sum is only performed over those possibilities.
|
|
684
674
|
|
|
685
675
|
# Work out the space to sum over
|
|
686
676
|
sum_space_set: List[Optional[Set[int]]] = [None] * self.number_of_rvs
|
|
@@ -719,11 +709,10 @@ class PGM:
|
|
|
719
709
|
precision: a limit on the render precision of floating point numbers.
|
|
720
710
|
max_state_digits: a limit on the number of digits when showing number of states as an integer.
|
|
721
711
|
"""
|
|
722
|
-
# limit
|
|
712
|
+
# Determine a limit to precision when displaying number of states
|
|
723
713
|
num_states: int = self.number_of_states
|
|
724
714
|
number_of_parameters = sum(function.number_of_parameters for function in self.functions)
|
|
725
715
|
number_of_nz_parameters = sum(function.number_of_parameters for function in self.non_zero_functions)
|
|
726
|
-
|
|
727
716
|
if math.log10(num_states) > max_state_digits:
|
|
728
717
|
log_states = math.log10(num_states)
|
|
729
718
|
exp = int(log_states)
|
|
@@ -731,7 +720,6 @@ class PGM:
|
|
|
731
720
|
num_states_str = f'{man:,.{precision}f}e+{exp}'
|
|
732
721
|
else:
|
|
733
722
|
num_states_str = f'{num_states:,}'
|
|
734
|
-
|
|
735
723
|
log_2_num_states = math.log2(num_states)
|
|
736
724
|
if (
|
|
737
725
|
log_2_num_states == 0
|
|
@@ -820,9 +808,9 @@ class PGM:
|
|
|
820
808
|
|
|
821
809
|
For a factor `f` the value of states[f.idx] is the search state.
|
|
822
810
|
Specifically:
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
811
|
+
state 0 => the factor has not been seen yet,
|
|
812
|
+
state 1 => the factor is seen but not fully processed,
|
|
813
|
+
state 2 => the factor is fully processed.
|
|
826
814
|
|
|
827
815
|
Args:
|
|
828
816
|
factor: the current Factor being checked.
|
|
@@ -1040,7 +1028,7 @@ class RandomVariable(Sequence[Indicator]):
|
|
|
1040
1028
|
|
|
1041
1029
|
def state_range(self) -> Iterable[int]:
|
|
1042
1030
|
"""
|
|
1043
|
-
Iterate over the state indexes of this random variable, in order.
|
|
1031
|
+
Iterate over the state indexes of this random variable, in ascending order.
|
|
1044
1032
|
|
|
1045
1033
|
Returns:
|
|
1046
1034
|
range(len(self))
|
|
@@ -1122,18 +1110,19 @@ class RandomVariable(Sequence[Indicator]):
|
|
|
1122
1110
|
|
|
1123
1111
|
def __eq__(self, other) -> bool:
|
|
1124
1112
|
"""
|
|
1125
|
-
Two random
|
|
1113
|
+
Two random variables are equal if they are the same object.
|
|
1126
1114
|
"""
|
|
1127
1115
|
return self is other
|
|
1128
1116
|
|
|
1129
1117
|
def equivalent(self, other: RandomVariable | Sequence[Indicator]) -> bool:
|
|
1130
1118
|
"""
|
|
1131
|
-
Two random variable are equivalent if their indicators are equal.
|
|
1132
|
-
random variable indexes and state indexes are checked.
|
|
1133
|
-
|
|
1119
|
+
Two random variable are equivalent if their indicators are equal.
|
|
1120
|
+
Only random variable indexes and state indexes are checked.
|
|
1134
1121
|
This ignores the names of the random variable and the names of their states.
|
|
1135
|
-
|
|
1136
|
-
|
|
1122
|
+
|
|
1123
|
+
Slot maps operate across `equivalent` random variables.
|
|
1124
|
+
This means indicators of equivalent random variables will work
|
|
1125
|
+
correctly in slot maps, even if from different PGMs.
|
|
1137
1126
|
|
|
1138
1127
|
Args:
|
|
1139
1128
|
other: either a random variable or a sequence of Indicators.
|
|
@@ -1181,7 +1170,8 @@ class RandomVariable(Sequence[Indicator]):
|
|
|
1181
1170
|
"""
|
|
1182
1171
|
Returns the first index of `value`.
|
|
1183
1172
|
Raises ValueError if the value is not present.
|
|
1184
|
-
|
|
1173
|
+
|
|
1174
|
+
This method is contracted by `Sequence[Indicator]`.
|
|
1185
1175
|
|
|
1186
1176
|
Warning:
|
|
1187
1177
|
This method is different to `self.idx`.
|
|
@@ -1198,7 +1188,10 @@ class RandomVariable(Sequence[Indicator]):
|
|
|
1198
1188
|
def count(self, value: Any) -> int:
|
|
1199
1189
|
"""
|
|
1200
1190
|
Returns the number of occurrences of `value`.
|
|
1201
|
-
|
|
1191
|
+
That is, if `value` is an indicator of this random variable
|
|
1192
|
+
then 1 is returned, otherwise 0 is returned.
|
|
1193
|
+
|
|
1194
|
+
This method is contracted by `Sequence[Indicator]`.
|
|
1202
1195
|
"""
|
|
1203
1196
|
if isinstance(value, Indicator):
|
|
1204
1197
|
if value.rv_idx == self._idx and 0 <= value.state_idx < len(self):
|
|
@@ -1210,15 +1203,16 @@ class RVMap(Sequence[RandomVariable]):
|
|
|
1210
1203
|
"""
|
|
1211
1204
|
Wrap a PGM to provide convenient access to PGM random variables.
|
|
1212
1205
|
|
|
1213
|
-
An RVMap of a PGM behaves
|
|
1214
|
-
|
|
1206
|
+
An RVMap of a PGM behaves like the PGM `rvs` property (sequence of
|
|
1207
|
+
RandomVariable objects), with additional access methods for the PGM's
|
|
1208
|
+
random variables.
|
|
1215
1209
|
|
|
1216
1210
|
If the underlying PGM is updated, then the RVMap will automatically update.
|
|
1217
1211
|
|
|
1218
|
-
|
|
1219
|
-
of each random variable.
|
|
1212
|
+
In addition to accessing a random variable by its index, an RVMap enables
|
|
1213
|
+
access to the PGM random variable via the name of each random variable.
|
|
1220
1214
|
|
|
1221
|
-
|
|
1215
|
+
For example, if `pgm.rvs[1]` is a random variable named `xray`, then:
|
|
1222
1216
|
```
|
|
1223
1217
|
rvs = RVMap(pgm)
|
|
1224
1218
|
|
|
@@ -1228,7 +1222,7 @@ class RVMap(Sequence[RandomVariable]):
|
|
|
1228
1222
|
xray = rvs.xray
|
|
1229
1223
|
```
|
|
1230
1224
|
|
|
1231
|
-
To use an RVMap on a PGM, the variable names must be unique across the PGM.
|
|
1225
|
+
To use an RVMap on a PGM, the random variable names must be unique across the PGM.
|
|
1232
1226
|
"""
|
|
1233
1227
|
|
|
1234
1228
|
def __init__(self, pgm: PGM, ignore_case: bool = False):
|
|
@@ -1248,28 +1242,6 @@ class RVMap(Sequence[RandomVariable]):
|
|
|
1248
1242
|
# This may raise an exception.
|
|
1249
1243
|
_ = self._rv_map
|
|
1250
1244
|
|
|
1251
|
-
def _clean_name(self, name: str) -> str:
|
|
1252
|
-
"""
|
|
1253
|
-
Adjust the case of the given name as needed.
|
|
1254
|
-
"""
|
|
1255
|
-
return name.lower() if self._ignore_case else name
|
|
1256
|
-
|
|
1257
|
-
@property
|
|
1258
|
-
def _rv_map(self) -> Dict[str, RandomVariable]:
|
|
1259
|
-
"""
|
|
1260
|
-
Get the cached rv map, updating as needed if the PGM changed.
|
|
1261
|
-
Returns:
|
|
1262
|
-
a mapping from random variable name to random variable
|
|
1263
|
-
"""
|
|
1264
|
-
if len(self.__rv_map) != len(self._pgm.rvs):
|
|
1265
|
-
# There is a difference between the map and the PGM - create a new map.
|
|
1266
|
-
self.__rv_map = {self._clean_name(rv.name): rv for rv in self._pgm.rvs}
|
|
1267
|
-
if len(self.__rv_map) != len(self._pgm.rvs):
|
|
1268
|
-
raise RuntimeError(f'random variable names are not unique')
|
|
1269
|
-
if not self._reserved_names.isdisjoint(self.__rv_map.keys()):
|
|
1270
|
-
raise RuntimeError(f'random variable names clash with reserved names.')
|
|
1271
|
-
return self.__rv_map
|
|
1272
|
-
|
|
1273
1245
|
def new_rv(self, name: str, states: Union[int, Sequence[State]]) -> RandomVariable:
|
|
1274
1246
|
"""
|
|
1275
1247
|
As per `PGM.new_rv`.
|
|
@@ -1304,6 +1276,29 @@ class RVMap(Sequence[RandomVariable]):
|
|
|
1304
1276
|
def __getattr__(self, rv_name: str) -> RandomVariable:
|
|
1305
1277
|
return self(rv_name)
|
|
1306
1278
|
|
|
1279
|
+
@property
|
|
1280
|
+
def _rv_map(self) -> Dict[str, RandomVariable]:
|
|
1281
|
+
"""
|
|
1282
|
+
Get the cached random variable map, updating as needed if the PGM changed.
|
|
1283
|
+
|
|
1284
|
+
Returns:
|
|
1285
|
+
a mapping from random variable name to random variable
|
|
1286
|
+
"""
|
|
1287
|
+
if len(self.__rv_map) != len(self._pgm.rvs):
|
|
1288
|
+
# There is a difference between the map and the PGM - create a new map.
|
|
1289
|
+
self.__rv_map = {self._clean_name(rv.name): rv for rv in self._pgm.rvs}
|
|
1290
|
+
if len(self.__rv_map) != len(self._pgm.rvs):
|
|
1291
|
+
raise RuntimeError(f'random variable names are not unique')
|
|
1292
|
+
if not self._reserved_names.isdisjoint(self.__rv_map.keys()):
|
|
1293
|
+
raise RuntimeError(f'random variable names clash with reserved names.')
|
|
1294
|
+
return self.__rv_map
|
|
1295
|
+
|
|
1296
|
+
def _clean_name(self, name: str) -> str:
|
|
1297
|
+
"""
|
|
1298
|
+
Adjust the case of the given name as needed.
|
|
1299
|
+
"""
|
|
1300
|
+
return name.lower() if self._ignore_case else name
|
|
1301
|
+
|
|
1307
1302
|
|
|
1308
1303
|
class Factor:
|
|
1309
1304
|
"""
|
|
@@ -1544,7 +1539,7 @@ class Factor:
|
|
|
1544
1539
|
self._potential_function = ClausePotentialFunction(self, key)
|
|
1545
1540
|
return self._potential_function
|
|
1546
1541
|
|
|
1547
|
-
def set_cpt(self, tolerance: float =
|
|
1542
|
+
def set_cpt(self, tolerance: float = DEFAULT_CPT_TOLERANCE) -> CPTPotentialFunction:
|
|
1548
1543
|
"""
|
|
1549
1544
|
Set to the potential function to a new `CPTPotentialFunction` object.
|
|
1550
1545
|
|
|
@@ -1820,7 +1815,7 @@ class PotentialFunction(ABC):
|
|
|
1820
1815
|
"""
|
|
1821
1816
|
...
|
|
1822
1817
|
|
|
1823
|
-
def is_cpt(self, tolerance=
|
|
1818
|
+
def is_cpt(self, tolerance=DEFAULT_CPT_TOLERANCE) -> bool:
|
|
1824
1819
|
"""
|
|
1825
1820
|
Is the potential function set with parameters values conforming to a
|
|
1826
1821
|
Conditional Probability Table.
|
|
@@ -2028,7 +2023,7 @@ class ZeroPotentialFunction(PotentialFunction):
|
|
|
2028
2023
|
def param_idx(self, key: Key) -> int:
|
|
2029
2024
|
return _natural_key_idx(self._shape, key)
|
|
2030
2025
|
|
|
2031
|
-
def is_cpt(self, tolerance=
|
|
2026
|
+
def is_cpt(self, tolerance=DEFAULT_CPT_TOLERANCE) -> bool:
|
|
2032
2027
|
return True
|
|
2033
2028
|
|
|
2034
2029
|
|
|
@@ -2836,7 +2831,7 @@ class ClausePotentialFunction(PotentialFunction):
|
|
|
2836
2831
|
else:
|
|
2837
2832
|
return None
|
|
2838
2833
|
|
|
2839
|
-
def is_cpt(self, tolerance=
|
|
2834
|
+
def is_cpt(self, tolerance=DEFAULT_CPT_TOLERANCE) -> bool:
|
|
2840
2835
|
"""
|
|
2841
2836
|
A ClausePotentialFunction can only be a CTP when all entries are zero.
|
|
2842
2837
|
"""
|
|
@@ -2930,7 +2925,7 @@ class CPTPotentialFunction(PotentialFunction):
|
|
|
2930
2925
|
def number_of_parameters(self) -> int:
|
|
2931
2926
|
return len(self._values)
|
|
2932
2927
|
|
|
2933
|
-
def is_cpt(self, tolerance=
|
|
2928
|
+
def is_cpt(self, tolerance=DEFAULT_CPT_TOLERANCE) -> bool:
|
|
2934
2929
|
if tolerance >= self._tolerance:
|
|
2935
2930
|
return True
|
|
2936
2931
|
else:
|
|
@@ -3015,12 +3010,11 @@ class CPTPotentialFunction(PotentialFunction):
|
|
|
3015
3010
|
|
|
3016
3011
|
def cpds(self) -> Iterable[Tuple[Instance, Sequence[float]]]:
|
|
3017
3012
|
"""
|
|
3018
|
-
Iterate over (parent_states, cpd) tuples.
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3013
|
+
Iterate over (parent_states, cpd) tuples. This will exclude zero CPDs.
|
|
3014
|
+
|
|
3015
|
+
Warning:
|
|
3016
|
+
Do not change CPDs to (or from) zero while iterating over them.
|
|
3017
|
+
|
|
3024
3018
|
Returns:
|
|
3025
3019
|
an iterator over pairs (instance, cpd) where,
|
|
3026
3020
|
instance: is indicates the state of the parent random variables.
|
|
@@ -3288,7 +3282,7 @@ def check_key(shape: Shape, key: Key) -> Instance:
|
|
|
3288
3282
|
A instance from the state space, as a tuple of state indexes, co-indexed with the given shape.
|
|
3289
3283
|
|
|
3290
3284
|
Raises:
|
|
3291
|
-
KeyError if the key is not valid.
|
|
3285
|
+
KeyError if the key is not valid for the given shape.
|
|
3292
3286
|
"""
|
|
3293
3287
|
_key: Instance = _key_to_instance(key)
|
|
3294
3288
|
if len(_key) != len(shape):
|
|
@@ -3336,8 +3330,8 @@ def rv_instances(*rvs: RandomVariable, flip: bool = False) -> Iterable[Instance]
|
|
|
3336
3330
|
flip: if true, then first random variable changes most quickly.
|
|
3337
3331
|
|
|
3338
3332
|
Returns:
|
|
3339
|
-
an iteration over
|
|
3340
|
-
co-indexed with the given random variables.
|
|
3333
|
+
an iteration over instances, each instance is a tuple of state
|
|
3334
|
+
indexes, co-indexed with the given random variables.
|
|
3341
3335
|
"""
|
|
3342
3336
|
shape = [len(rv) for rv in rvs]
|
|
3343
3337
|
return _combos_ranges(shape, flip=not flip)
|
|
@@ -3384,6 +3378,10 @@ def _natural_key_idx(shape: Shape, key: Key) -> int:
|
|
|
3384
3378
|
"""
|
|
3385
3379
|
What is the natural index of the given key, assuming the given shape.
|
|
3386
3380
|
|
|
3381
|
+
The natural index of an instance is defined as the index of the
|
|
3382
|
+
instance if all instances for the shape are enumerated as per
|
|
3383
|
+
`rv_instances`.
|
|
3384
|
+
|
|
3387
3385
|
Args:
|
|
3388
3386
|
shape: the shape defining the state space.
|
|
3389
3387
|
key: a key into the state space.
|
ck/pgm_compiler/pgm_compiler.py
CHANGED
|
@@ -7,7 +7,7 @@ from ck.pgm_circuit import PGMCircuit
|
|
|
7
7
|
class PGMCompiler(Protocol):
|
|
8
8
|
def __call__(self, pgm: PGM, *, const_parameters: bool = True) -> PGMCircuit:
|
|
9
9
|
"""
|
|
10
|
-
A PGM compiler
|
|
10
|
+
A PGM compiler compiles a PGM to an arithmetic circuit.
|
|
11
11
|
|
|
12
12
|
Args:
|
|
13
13
|
pgm: The PGM to compile.
|
|
@@ -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-b7kylfcw\\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
|
|
@@ -192,7 +192,7 @@ def _make_spanning_tree_small_root(cost: NDArrayFloat64, clusters: List[Set[int]
|
|
|
192
192
|
Returns:
|
|
193
193
|
(spanning_tree, root_index)
|
|
194
194
|
|
|
195
|
-
spanning_tree: is a spanning tree represented as a list of nodes, the list is
|
|
195
|
+
spanning_tree: is a spanning tree represented as a list of nodes, the list is co-indexed with
|
|
196
196
|
the given cost matrix, each node is a list of children, each child being
|
|
197
197
|
represented as an index into the list of nodes.
|
|
198
198
|
|
|
@@ -219,7 +219,7 @@ def _make_spanning_tree_arbitrary_root(cost: NDArrayFloat64) -> Tuple[List[List[
|
|
|
219
219
|
Returns:
|
|
220
220
|
(spanning_tree, root_index)
|
|
221
221
|
|
|
222
|
-
spanning_tree: is a spanning tree represented as a list of nodes, the list is
|
|
222
|
+
spanning_tree: is a spanning tree represented as a list of nodes, the list is co-indexed with
|
|
223
223
|
the given cost matrix, each node is a list of children, each child being
|
|
224
224
|
represented as an index into the list of nodes.
|
|
225
225
|
|
|
@@ -243,7 +243,7 @@ def _make_spanning_tree_at_root(
|
|
|
243
243
|
root_custer_index: a nominated root cluster to be the root of the tree.
|
|
244
244
|
|
|
245
245
|
Returns:
|
|
246
|
-
a spanning tree represented as a list of nodes, the list is
|
|
246
|
+
a spanning tree represented as a list of nodes, the list is co-indexed with
|
|
247
247
|
the given cost matrix, each node is a list of children, each child being
|
|
248
248
|
represented as an index into the list of nodes. The root node is the
|
|
249
249
|
index `root_custer_index` as passed to this function.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Sequence, Iterable, Tuple, Dict
|
|
1
|
+
from typing import Sequence, Iterable, Tuple, Dict
|
|
2
2
|
|
|
3
3
|
from ck.pgm import RandomVariable, Indicator, Instance
|
|
4
4
|
from ck.probability.probability_space import ProbabilitySpace, Condition, check_condition
|
|
@@ -10,6 +10,8 @@ class EmpiricalProbabilitySpace(ProbabilitySpace):
|
|
|
10
10
|
Enable probabilistic queries over a sample from a sample space.
|
|
11
11
|
Note that this is not necessarily an efficient approach to calculating probabilities and statistics.
|
|
12
12
|
|
|
13
|
+
This probability space treats each of the samples as equally weighted.
|
|
14
|
+
|
|
13
15
|
Assumes:
|
|
14
16
|
len(sample) == len(rvs), for each sample in samples.
|
|
15
17
|
0 <= sample[i] < len(rvs[i]), for each sample in samples, for i in range(len(rvs)).
|
|
@@ -19,7 +21,7 @@ class EmpiricalProbabilitySpace(ProbabilitySpace):
|
|
|
19
21
|
samples: instances (state indexes) that are samples from the given rvs.
|
|
20
22
|
"""
|
|
21
23
|
self._rvs: Sequence[RandomVariable] = tuple(rvs)
|
|
22
|
-
self._samples:
|
|
24
|
+
self._samples: Sequence[Instance] = tuple(samples)
|
|
23
25
|
self._rv_idx_to_sample_idx: Dict[int, int] = {
|
|
24
26
|
rv.idx: i
|
|
25
27
|
for i, rv in enumerate(self._rvs)
|
|
@@ -47,4 +49,3 @@ class EmpiricalProbabilitySpace(ProbabilitySpace):
|
|
|
47
49
|
@property
|
|
48
50
|
def z(self) -> float:
|
|
49
51
|
return len(self._samples)
|
|
50
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from typing import Sequence,
|
|
1
|
+
from typing import Sequence, Tuple
|
|
2
2
|
|
|
3
|
-
from ck.pgm import RandomVariable, Indicator,
|
|
3
|
+
from ck.pgm import RandomVariable, Indicator, PGM
|
|
4
4
|
from ck.probability.probability_space import ProbabilitySpace, Condition, check_condition
|
|
5
5
|
|
|
6
6
|
|
|
@@ -12,6 +12,11 @@ class PGMProbabilitySpace(ProbabilitySpace):
|
|
|
12
12
|
|
|
13
13
|
Args:
|
|
14
14
|
pgm: The PGM to query.
|
|
15
|
+
|
|
16
|
+
Warning:
|
|
17
|
+
The resulting probability space assumes that the value of the partition
|
|
18
|
+
function, `z`, remains constant for the lifetime of the PGM. If the value
|
|
19
|
+
changes, then probabilities and statistics will be incorrect.
|
|
15
20
|
"""
|
|
16
21
|
self._pgm = pgm
|
|
17
22
|
self._z = None
|
|
@@ -29,4 +34,3 @@ class PGMProbabilitySpace(ProbabilitySpace):
|
|
|
29
34
|
if self._z is None:
|
|
30
35
|
self._z = self._pgm.value_product_indicators()
|
|
31
36
|
return self._z
|
|
32
|
-
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
"""
|
|
2
|
-
An abstract class for object providing probabilities.
|
|
3
|
-
"""
|
|
4
1
|
import math
|
|
5
2
|
from abc import ABC, abstractmethod
|
|
6
3
|
from itertools import chain
|
|
7
|
-
from typing import Sequence, Tuple, Iterable, Callable
|
|
4
|
+
from typing import Sequence, Tuple, Iterable, Callable, TypeAlias
|
|
8
5
|
|
|
9
6
|
import numpy as np
|
|
10
7
|
|
|
@@ -13,8 +10,20 @@ from ck.utils.iter_extras import combos as _combos
|
|
|
13
10
|
from ck.utils.map_set import MapSet
|
|
14
11
|
from ck.utils.np_extras import dtype_for_number_of_states, NDArrayFloat64, DTypeStates, NDArrayNumeric
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
Condition =
|
|
13
|
+
Condition: TypeAlias = None | Indicator | Iterable[Indicator]
|
|
14
|
+
Condition.__doc__ = \
|
|
15
|
+
"""
|
|
16
|
+
Type defining a condition. A condition is logically a set of
|
|
17
|
+
indicators, each indicator representing a random variable being in some state.
|
|
18
|
+
|
|
19
|
+
If multiple indicators of the same random variable appear in
|
|
20
|
+
a condition, then they are interpreted as
|
|
21
|
+
a disjunction, otherwise indicators are interpreted as
|
|
22
|
+
a conjunction. E.g., the condition (X=0, Y=1, Y=3) means
|
|
23
|
+
X=0 and (Y=1 or Y=3).
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
_NAN: float = np.nan # Not-a-number (i.e., the result of an invalid calculation).
|
|
18
27
|
|
|
19
28
|
|
|
20
29
|
class ProbabilitySpace(ABC):
|
|
@@ -41,13 +50,9 @@ class ProbabilitySpace(ABC):
|
|
|
41
50
|
"""
|
|
42
51
|
Return the weight of instances matching the given condition.
|
|
43
52
|
|
|
44
|
-
If multiple indicators of the same random variable appear in
|
|
45
|
-
the parameter 'indicators' then they are interpreted as
|
|
46
|
-
a disjunction, otherwise indicators are interpreted as
|
|
47
|
-
a conjunction. E.g.: X=0, Y=1, Y=3 means X=0 and (Y=1 or Y=3)
|
|
48
|
-
|
|
49
53
|
Args:
|
|
50
|
-
condition:
|
|
54
|
+
condition: a condition restricting the instances that are
|
|
55
|
+
considered in the result.
|
|
51
56
|
"""
|
|
52
57
|
|
|
53
58
|
@property
|
|
@@ -56,6 +61,7 @@ class ProbabilitySpace(ABC):
|
|
|
56
61
|
"""
|
|
57
62
|
Return the summed weight of all instances.
|
|
58
63
|
This is equivalent to self.wmc(), with no arguments.
|
|
64
|
+
This is also known as the "partition function".
|
|
59
65
|
"""
|
|
60
66
|
|
|
61
67
|
def probability(self, *indicators: Indicator, condition: Condition = ()) -> float:
|
|
@@ -80,11 +86,11 @@ class ProbabilitySpace(ABC):
|
|
|
80
86
|
if len(condition) == 0:
|
|
81
87
|
z = self.z
|
|
82
88
|
if z <= 0:
|
|
83
|
-
return
|
|
89
|
+
return _NAN
|
|
84
90
|
else:
|
|
85
91
|
z = self.wmc(*condition)
|
|
86
92
|
if z <= 0:
|
|
87
|
-
return
|
|
93
|
+
return _NAN
|
|
88
94
|
|
|
89
95
|
# Combine the indicators with the condition
|
|
90
96
|
# If a variable is mentioned in both the indicators and condition, then
|
|
@@ -617,6 +623,6 @@ def _normalise_marginal(distribution: NDArrayFloat64) -> None:
|
|
|
617
623
|
"""
|
|
618
624
|
total = np.sum(distribution)
|
|
619
625
|
if total <= 0:
|
|
620
|
-
distribution[:] =
|
|
626
|
+
distribution[:] = _NAN
|
|
621
627
|
elif total != 1:
|
|
622
628
|
distribution /= total
|
ck/sampling/sampler_support.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from itertools import count
|
|
3
|
-
from typing import Callable, Sequence, Optional, Set, Tuple, Dict, Collection
|
|
3
|
+
from typing import Callable, Sequence, Optional, Set, Tuple, Dict, Collection, TypeAlias
|
|
4
4
|
|
|
5
5
|
from ck.pgm import Instance, RandomVariable, Indicator
|
|
6
6
|
from ck.pgm_circuit.program_with_slotmap import ProgramWithSlotmap
|
|
@@ -10,10 +10,13 @@ from ck.utils.map_set import MapSet
|
|
|
10
10
|
from ck.utils.np_extras import NDArrayStates, NDArrayNumeric
|
|
11
11
|
from ck.utils.random_extras import Random
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
YieldF: TypeAlias = Callable[[NDArrayStates], int] | Callable[[NDArrayStates], Instance]
|
|
14
|
+
YieldF.__doc__ = \
|
|
15
|
+
"""
|
|
16
|
+
Type of a yield function. Support for a sampler.
|
|
17
|
+
A yield function may be used to implement a sampler's iterator, thus
|
|
18
|
+
it provides an Instance or single state index.
|
|
19
|
+
"""
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
@dataclass
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: compiled-knowledge
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.0a24
|
|
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
|