@@ -2226,11 +2226,11 @@ class ICARRV(RandomVariable):
2226
2226
dtype = "floatX"
2227
2227
_print_name = ("ICAR" , "\\ operatorname{ICAR}" )
2228
2228
2229
- def __call__ (self , W , node1 , node2 , N , sigma , zero_sum_strength , size = None , ** kwargs ):
2230
- return super ().__call__ (W , node1 , node2 , N , sigma , zero_sum_strength , size = size , ** kwargs )
2229
+ def __call__ (self , W , node1 , node2 , N , sigma , zero_sum_stdev , size = None , ** kwargs ):
2230
+ return super ().__call__ (W , node1 , node2 , N , sigma , zero_sum_stdev , size = size , ** kwargs )
2231
2231
2232
2232
@classmethod
2233
- def rng_fn (cls , rng , size , W , node1 , node2 , N , sigma , zero_sum_strength ):
2233
+ def rng_fn (cls , rng , size , W , node1 , node2 , N , sigma , zero_sum_stdev ):
2234
2234
raise NotImplementedError ("Cannot sample from ICAR prior" )
2235
2235
2236
2236
@@ -2240,40 +2240,41 @@ def rng_fn(cls, rng, size, W, node1, node2, N, sigma, zero_sum_strength):
2240
2240
class ICAR (Continuous ):
2241
2241
r"""
2242
2242
The intrinsic conditional autoregressive prior. It is primarily used to model
2243
- covariance between neighboring areas on large datasets . It is a special case
2243
+ covariance between neighboring areas. It is a special case
2244
2244
of the :class:`~pymc.CAR` distribution where alpha is set to 1.
2245
2245
2246
2246
The log probability density function is
2247
2247
2248
2248
.. math::
2249
- f(\\ phi| W,\ \sigma) =
2250
- -\frac{1}{2\\ sigma^{2}} \\ sum_{i\\ sim j} (\\ phi_{i} - \ \phi_{j})^2 -
2251
- \frac{1}{2}*\frac{\\ sum_{i}{\\ phi_{i}}}{0.001N}^{2} - \\ ln{\ \sqrt{2\\pi}} -
2252
- \\ ln{0.001N}
2249
+ f(\phi| W,\sigma) =
2250
+ -\frac{1}{2\sigma^{2}} \sum_{i\sim j} (\phi_{i} - \phi_{j})^2 -
2251
+ \frac{1}{2}*\frac{\sum_{i}{\phi_{i}}}{0.001N}^{2} - \ln{\sqrt{2\\pi}} -
2252
+ \ln{0.001N}
2253
2253
2254
2254
The first term represents the spatial covariance component. Each $\\phi_{i}$ is penalized
2255
2255
based on the square distance from each of its neighbors. The notation $i\\sim j$
2256
2256
indicates a sum over all the neighbors of $\\phi_{i}$. The last three terms are the
2257
2257
Normal log density function where the mean is zero and the standard deviation is
2258
- $N * 0.001$ (where N is the length of the vector $\\phi$). This component imposed the zero-sum
2259
- constraint by finding the sum of the vector $\\phi$ and penalizing based on its
2260
- distance from zero.
2258
+ $N * 0.001$ (where N is the length of the vector $\\phi$). This component imposes
2259
+ a zero-sum constraint by finding the sum of the vector $\\phi$ and penalizing based
2260
+ on its distance from zero.
2261
2261
2262
2262
Parameters
2263
2263
----------
2264
2264
W : ndarray of int
2265
2265
Symmetric adjacency matrix of 1s and 0s indicating adjacency between elements.
2266
- Must pass either W or both node1 and node2.
2267
2266
2268
2267
sigma : scalar, default 1
2269
2268
Standard deviation of the vector of phi's. Putting a prior on sigma
2270
2269
will result in a centered parameterization. In most cases, it is
2271
2270
preferable to use a non-centered parameterization by using the default
2272
2271
value and multiplying the resulting phi's by sigma. See the example below.
2273
2272
2274
- zero_sum_strength : scalar, default 0.001
2275
- Controls how strongly to enforce the zero-sum constraint. It sets the
2276
- standard deviation of a normal density function with mean zero.
2273
+ zero_sum_stdev : scalar, default 0.001
2274
+ Controls how strongly to enforce the zero-sum constraint. The sum of
2275
+ phi is normally distributed with a mean of zero and small standard deviation.
2276
+ This parameter sets the standard deviation of a normal density function with
2277
+ mean zero.
2277
2278
2278
2279
2279
2280
Examples
@@ -2289,25 +2290,23 @@ class ICAR(Continuous):
2289
2290
# 4x4 adjacency matrix
2290
2291
# arranged in a square lattice
2291
2292
2292
- W = np.array([[0,1,0,1],
2293
- [1,0,1,0],
2294
- [0,1,0,1],
2295
- [1,0,1,0]])
2293
+ W = np.array([
2294
+ [0,1,0,1],
2295
+ [1,0,1,0],
2296
+ [0,1,0,1],
2297
+ [1,0,1,0]
2298
+ ])
2296
2299
2297
2300
# centered parameterization
2298
-
2299
2301
with pm.Model():
2300
- sigma = pm.Exponential('sigma',1)
2301
- phi = pm.ICAR('phi',W=W,sigma=sigma)
2302
-
2302
+ sigma = pm.Exponential('sigma', 1)
2303
+ phi = pm.ICAR('phi', W=W, sigma=sigma)
2303
2304
mu = phi
2304
2305
2305
2306
# non-centered parameterization
2306
-
2307
2307
with pm.Model():
2308
- sigma = pm.Exponential('sigma',1)
2309
- phi = pm.ICAR('phi',W=W)
2310
-
2308
+ sigma = pm.Exponential('sigma', 1)
2309
+ phi = pm.ICAR('phi', W=W)
2311
2310
mu = sigma * phi
2312
2311
2313
2312
References
@@ -2326,12 +2325,7 @@ class ICAR(Continuous):
2326
2325
rv_op = icar
2327
2326
2328
2327
@classmethod
2329
- def dist (cls , W , sigma = 1 , zero_sum_strength = 0.001 , ** kwargs ):
2330
- # check that adjacency matrix is two dimensional,
2331
- # square,
2332
- # symmetrical
2333
- # and composed of 1s or 0s.
2334
-
2328
+ def dist (cls , W , sigma = 1 , zero_sum_stdev = 0.001 , ** kwargs ):
2335
2329
if not W .ndim == 2 :
2336
2330
raise ValueError ("W must be matrix with ndim=2" )
2337
2331
@@ -2345,6 +2339,12 @@ def dist(cls, W, sigma=1, zero_sum_strength=0.001, **kwargs):
2345
2339
raise ValueError ("W must be composed of only 1s and 0s" )
2346
2340
2347
2341
# convert adjacency matrix to edgelist representation
2342
+ # An edgelist is a pair of lists.
2343
+ # If node i and node j are connected then one list
2344
+ # will contain i and the other will contain j at the same
2345
+ # index value.
2346
+ # We only use the lower triangle here because adjacency
2347
+ # is a undirected connection.
2348
2348
2349
2349
node1 , node2 = np .where (np .tril (W ) == 1 )
2350
2350
@@ -2356,30 +2356,25 @@ def dist(cls, W, sigma=1, zero_sum_strength=0.001, **kwargs):
2356
2356
N = pt .shape (W )[0 ]
2357
2357
N = pt .as_tensor_variable (N )
2358
2358
2359
- # check on sigma
2360
-
2361
2359
sigma = pt .as_tensor_variable (floatX (sigma ))
2360
+ zero_sum_stdev = pt .as_tensor_variable (floatX (zero_sum_stdev ))
2362
2361
2363
- # check on centering_strength
2364
-
2365
- zero_sum_strength = pt .as_tensor_variable (floatX (zero_sum_strength ))
2366
-
2367
- return super ().dist ([W , node1 , node2 , N , sigma , zero_sum_strength ], ** kwargs )
2362
+ return super ().dist ([W , node1 , node2 , N , sigma , zero_sum_stdev ], ** kwargs )
2368
2363
2369
- def moment (rv , size , W , node1 , node2 , N , sigma , zero_sum_strength ):
2364
+ def moment (rv , size , W , node1 , node2 , N , sigma , zero_sum_stdev ):
2370
2365
return pt .zeros (N )
2371
2366
2372
- def logp (value , W , node1 , node2 , N , sigma , zero_sum_strength ):
2367
+ def logp (value , W , node1 , node2 , N , sigma , zero_sum_stdev ):
2373
2368
pairwise_difference = (- 1 / (2 * sigma ** 2 )) * pt .sum (
2374
2369
pt .square (value [node1 ] - value [node2 ])
2375
2370
)
2376
- soft_center = (
2377
- - 0.5 * pt .pow (pt .sum (value ) / (zero_sum_strength * N ), 2 )
2371
+ zero_sum = (
2372
+ - 0.5 * pt .pow (pt .sum (value ) / (zero_sum_stdev * N ), 2 )
2378
2373
- pt .log (pt .sqrt (2.0 * np .pi ))
2379
- - pt .log (zero_sum_strength * N )
2374
+ - pt .log (zero_sum_stdev * N )
2380
2375
)
2381
2376
2382
- return check_parameters (pairwise_difference + soft_center , sigma > 0 , msg = "sigma > 0" )
2377
+ return check_parameters (pairwise_difference + zero_sum , sigma > 0 , msg = "sigma > 0" )
2383
2378
2384
2379
2385
2380
class StickBreakingWeightsRV (RandomVariable ):
0 commit comments