@@ -550,35 +550,33 @@ def __init__(
550
550
self .name = self ._validate_name (name )
551
551
self .check_bounds = check_bounds
552
552
553
- self ._initial_values : Dict [TensorVariable , Optional [Union [np .ndarray , Variable , str ]]] = {}
554
-
555
553
if self .parent is not None :
556
554
self .named_vars = treedict (parent = self .parent .named_vars )
555
+ self .named_vars_to_dims = treedict (parent = self .parent .named_vars_to_dims )
557
556
self .values_to_rvs = treedict (parent = self .parent .values_to_rvs )
558
557
self .rvs_to_values = treedict (parent = self .parent .rvs_to_values )
559
558
self .rvs_to_transforms = treedict (parent = self .parent .rvs_to_transforms )
560
559
self .rvs_to_total_sizes = treedict (parent = self .parent .rvs_to_total_sizes )
560
+ self .rvs_to_initial_values = treedict (parent = self .parent .rvs_to_initial_values )
561
561
self .free_RVs = treelist (parent = self .parent .free_RVs )
562
562
self .observed_RVs = treelist (parent = self .parent .observed_RVs )
563
- self .auto_deterministics = treelist (parent = self .parent .auto_deterministics )
564
563
self .deterministics = treelist (parent = self .parent .deterministics )
565
564
self .potentials = treelist (parent = self .parent .potentials )
566
565
self ._coords = self .parent ._coords
567
- self ._RV_dims = treedict (parent = self .parent ._RV_dims )
568
566
self ._dim_lengths = self .parent ._dim_lengths
569
567
else :
570
568
self .named_vars = treedict ()
569
+ self .named_vars_to_dims = treedict ()
571
570
self .values_to_rvs = treedict ()
572
571
self .rvs_to_values = treedict ()
573
572
self .rvs_to_transforms = treedict ()
574
573
self .rvs_to_total_sizes = treedict ()
574
+ self .rvs_to_initial_values = treedict ()
575
575
self .free_RVs = treelist ()
576
576
self .observed_RVs = treelist ()
577
- self .auto_deterministics = treelist ()
578
577
self .deterministics = treelist ()
579
578
self .potentials = treelist ()
580
579
self ._coords = {}
581
- self ._RV_dims = treedict ()
582
580
self ._dim_lengths = {}
583
581
self .add_coords (coords )
584
582
@@ -972,7 +970,11 @@ def RV_dims(self) -> Dict[str, Tuple[Union[str, None], ...]]:
972
970
973
971
Entries in the tuples may be ``None``, if the RV dimension was not given a name.
974
972
"""
975
- return self ._RV_dims
973
+ warnings .warn (
974
+ "Model.RV_dims is deprecated. User Model.named_vars_to_dims instead." ,
975
+ FutureWarning ,
976
+ )
977
+ return self .named_vars_to_dims
976
978
977
979
@property
978
980
def coords (self ) -> Dict [str , Union [Tuple , None ]]:
@@ -1124,15 +1126,18 @@ def initial_values(self) -> Dict[TensorVariable, Optional[Union[np.ndarray, Vari
1124
1126
Keys are the random variables (as returned by e.g. ``pm.Uniform()``) and
1125
1127
values are the numeric/symbolic initial values, strings denoting the strategy to get them, or None.
1126
1128
"""
1127
- return self ._initial_values
1129
+ warnings .warn (
1130
+ "Model.initial_values is deprecated. Use Model.rvs_to_initial_values instead."
1131
+ )
1132
+ return self .rvs_to_initial_values
1128
1133
1129
1134
def set_initval (self , rv_var , initval ):
1130
1135
"""Sets an initial value (strategy) for a random variable."""
1131
1136
if initval is not None and not isinstance (initval , (Variable , str )):
1132
1137
# Convert scalars or array-like inputs to ndarrays
1133
1138
initval = rv_var .type .filter (initval )
1134
1139
1135
- self .initial_values [rv_var ] = initval
1140
+ self .rvs_to_initial_values [rv_var ] = initval
1136
1141
1137
1142
def set_data (
1138
1143
self ,
@@ -1167,7 +1172,7 @@ def set_data(
1167
1172
if isinstance (values , list ):
1168
1173
values = np .array (values )
1169
1174
values = convert_observed_data (values )
1170
- dims = self .RV_dims .get (name , None ) or ()
1175
+ dims = self .named_vars_to_dims .get (name , None ) or ()
1171
1176
coords = coords or {}
1172
1177
1173
1178
if values .ndim != shared_object .ndim :
@@ -1257,7 +1262,7 @@ def set_data(
1257
1262
shared_object .set_value (values )
1258
1263
1259
1264
def register_rv (
1260
- self , rv_var , name , data = None , total_size = None , dims = None , transform = UNSET , initval = None
1265
+ self , rv_var , name , observed = None , total_size = None , dims = None , transform = UNSET , initval = None
1261
1266
):
1262
1267
"""Register an (un)observed random variable with the model.
1263
1268
@@ -1266,9 +1271,8 @@ def register_rv(
1266
1271
rv_var: TensorVariable
1267
1272
name: str
1268
1273
Intended name for the model variable.
1269
- data: array_like (optional)
1270
- If data is provided, the variable is observed. If None,
1271
- the variable is unobserved.
1274
+ observed: array_like (optional)
1275
+ Data values for observed variables.
1272
1276
total_size: scalar
1273
1277
upscales logp of variable with ``coef = total_size/var.shape[0]``
1274
1278
dims: tuple
@@ -1295,31 +1299,31 @@ def register_rv(
1295
1299
if dname not in self .dim_lengths :
1296
1300
self .add_coord (dname , values = None , length = rv_var .shape [d ])
1297
1301
1298
- if data is None :
1302
+ if observed is None :
1299
1303
self .free_RVs .append (rv_var )
1300
1304
self .create_value_var (rv_var , transform )
1301
- self .add_random_variable (rv_var , dims )
1305
+ self .add_named_variable (rv_var , dims )
1302
1306
self .set_initval (rv_var , initval )
1303
1307
else :
1304
1308
if (
1305
- isinstance (data , Variable )
1306
- and not isinstance (data , (GenTensorVariable , Minibatch ))
1307
- and data .owner is not None
1309
+ isinstance (observed , Variable )
1310
+ and not isinstance (observed , (GenTensorVariable , Minibatch ))
1311
+ and observed .owner is not None
1308
1312
# The only Aesara operation we allow on observed data is type casting
1309
1313
# Although we could allow for any graph that does not depend on other RVs
1310
1314
and not (
1311
- isinstance (data .owner .op , Elemwise )
1312
- and isinstance (data .owner .op .scalar_op , Cast )
1315
+ isinstance (observed .owner .op , Elemwise )
1316
+ and isinstance (observed .owner .op .scalar_op , Cast )
1313
1317
)
1314
1318
):
1315
1319
raise TypeError (
1316
1320
"Variables that depend on other nodes cannot be used for observed data."
1317
- f"The data variable was: { data } "
1321
+ f"The data variable was: { observed } "
1318
1322
)
1319
1323
1320
1324
# `rv_var` is potentially changed by `make_obs_var`,
1321
1325
# for example into a new graph for imputation of missing data.
1322
- rv_var = self .make_obs_var (rv_var , data , dims , transform )
1326
+ rv_var = self .make_obs_var (rv_var , observed , dims , transform )
1323
1327
1324
1328
return rv_var
1325
1329
@@ -1425,14 +1429,15 @@ def make_obs_var(
1425
1429
observed_rv_var .tag .observations = nonmissing_data
1426
1430
1427
1431
self .create_value_var (observed_rv_var , transform = None , value_var = nonmissing_data )
1428
- self .add_random_variable (observed_rv_var )
1432
+ self .add_named_variable (observed_rv_var )
1429
1433
self .observed_RVs .append (observed_rv_var )
1430
1434
1431
1435
# Create deterministic that combines observed and missing
1436
+ # Note: This can widely increase memory consumption during sampling for large datasets
1432
1437
rv_var = at .zeros (data .shape )
1433
1438
rv_var = at .set_subtensor (rv_var [mask .nonzero ()], missing_rv_var )
1434
1439
rv_var = at .set_subtensor (rv_var [antimask_idx ], observed_rv_var )
1435
- rv_var = Deterministic (name , rv_var , self , dims , auto = True )
1440
+ rv_var = Deterministic (name , rv_var , self , dims )
1436
1441
1437
1442
else :
1438
1443
if sps .issparse (data ):
@@ -1441,7 +1446,7 @@ def make_obs_var(
1441
1446
data = at .as_tensor_variable (data , name = name )
1442
1447
rv_var .tag .observations = data
1443
1448
self .create_value_var (rv_var , transform = None , value_var = data )
1444
- self .add_random_variable (rv_var , dims )
1449
+ self .add_named_variable (rv_var , dims )
1445
1450
self .observed_RVs .append (rv_var )
1446
1451
1447
1452
return rv_var
@@ -1481,15 +1486,18 @@ def create_value_var(
1481
1486
value_var .tag .test_value = transform .forward (
1482
1487
value_var , * rv_var .owner .inputs
1483
1488
).tag .test_value
1484
- self .named_vars [value_var .name ] = value_var
1485
1489
self .rvs_to_transforms [rv_var ] = transform
1486
1490
self .rvs_to_values [rv_var ] = value_var
1487
1491
self .values_to_rvs [value_var ] = rv_var
1488
1492
1489
1493
return value_var
1490
1494
1491
- def add_random_variable (self , var , dims : Optional [Tuple [Union [str , None ], ...]] = None ):
1492
- """Add a random variable to the named variables of the model."""
1495
+ def add_named_variable (self , var , dims : Optional [Tuple [Union [str , None ], ...]] = None ):
1496
+ """Add a random graph variable to the named variables of the model.
1497
+
1498
+ This can include several types of variables such basic_RVs, Data, Deterministics,
1499
+ and Potentials.
1500
+ """
1493
1501
if self .named_vars .tree_contains (var .name ):
1494
1502
raise ValueError (f"Variable name { var .name } already exists." )
1495
1503
@@ -1501,7 +1509,7 @@ def add_random_variable(self, var, dims: Optional[Tuple[Union[str, None], ...]]
1501
1509
raise ValueError (f"Dimension { dim } is not specified in `coords`." )
1502
1510
if any (var .name == dim for dim in dims ):
1503
1511
raise ValueError (f"Variable `{ var .name } ` has the same name as its dimension label." )
1504
- self ._RV_dims [var .name ] = dims
1512
+ self .named_vars_to_dims [var .name ] = dims
1505
1513
1506
1514
self .named_vars [var .name ] = var
1507
1515
if not hasattr (self , self .name_of (var .name )):
@@ -1705,14 +1713,17 @@ def check_start_vals(self, start):
1705
1713
None
1706
1714
"""
1707
1715
start_points = [start ] if isinstance (start , dict ) else start
1716
+
1717
+ value_names_to_dtypes = {value .name : value .dtype for value in self .value_vars }
1718
+ value_names_set = set (value_names_to_dtypes .keys ())
1708
1719
for elem in start_points :
1709
1720
1710
1721
for k , v in elem .items ():
1711
- elem [k ] = np .asarray (v , dtype = self [k ]. dtype )
1722
+ elem [k ] = np .asarray (v , dtype = value_names_to_dtypes [k ])
1712
1723
1713
- if not set (elem .keys ()).issubset (self . named_vars . keys () ):
1714
- extra_keys = ", " .join (set (elem .keys ()) - set ( self . named_vars . keys ()) )
1715
- valid_keys = ", " .join (self . named_vars . keys () )
1724
+ if not set (elem .keys ()).issubset (value_names_set ):
1725
+ extra_keys = ", " .join (set (elem .keys ()) - value_names_set )
1726
+ valid_keys = ", " .join (value_names_set )
1716
1727
raise KeyError (
1717
1728
"Some start parameters do not appear in the model!\n "
1718
1729
f"Valid keys are: { valid_keys } , but { extra_keys } was supplied"
@@ -1899,7 +1910,7 @@ def Point(*args, filter_model_vars=False, **kwargs) -> Dict[str, np.ndarray]:
1899
1910
}
1900
1911
1901
1912
1902
- def Deterministic (name , var , model = None , dims = None , auto = False ):
1913
+ def Deterministic (name , var , model = None , dims = None ):
1903
1914
"""Create a named deterministic variable.
1904
1915
1905
1916
Deterministic nodes are only deterministic given all of their inputs, i.e.
@@ -1962,11 +1973,8 @@ def Deterministic(name, var, model=None, dims=None, auto=False):
1962
1973
"""
1963
1974
model = modelcontext (model )
1964
1975
var = var .copy (model .name_for (name ))
1965
- if auto :
1966
- model .auto_deterministics .append (var )
1967
- else :
1968
- model .deterministics .append (var )
1969
- model .add_random_variable (var , dims )
1976
+ model .deterministics .append (var )
1977
+ model .add_named_variable (var , dims )
1970
1978
1971
1979
from pymc .printing import str_for_potential_or_deterministic
1972
1980
@@ -1998,7 +2006,7 @@ def Potential(name, var, model=None):
1998
2006
model = modelcontext (model )
1999
2007
var .name = model .name_for (name )
2000
2008
model .potentials .append (var )
2001
- model .add_random_variable (var )
2009
+ model .add_named_variable (var )
2002
2010
2003
2011
from pymc .printing import str_for_potential_or_deterministic
2004
2012
0 commit comments