I am simulating spatially distributed points in $\mathbb{R}^2$ with intensity $\lambda$ (units 1/area), which act as circle origins with radii being a random variable $R_k$. Given the distance to the $k$th nearest neighbor $D_k$ (see e.g. Moltchanov 2012), the contact probability of the circle located in the origin (denoted with subscript 0) with the circle originated as the $k$th neighbor is \begin{equation} P(\mathrm{contact \ with \ circle\ } k) = P(R_0 + R_k \ge D_k). \end{equation} As a MWE, I will use $R_k \sim \mathcal{N}(\mu, \sigma^2)$ and Poisson process for spatial. The distance distribution then follows a generalized Gamma distribution \begin{equation} f_{D_k}(r) = \frac{2(\lambda \pi r^2)^k}{r \Gamma(k)}e^{-\lambda \pi r^2}, \quad r > 0, \ k=1,2,\dots \end{equation} The sum distribution of two identical normally distributed radii is \begin{equation} R_0 + R_k \equiv Z \sim \mathcal{N}(2 \mu, 2 \sigma^2) \end{equation} The contact probability is then calculated from \begin{equation} P(\mathrm{contact \ with \ circle\ } k) = \int_{0^+}^\infty f_Z(r) F_{D_k}(r) dr \end{equation} The probability of the circle in the origin not being in contact with any of the $M$ nearest neighbors is then \begin{equation} P(\mathrm{not \ in \ contact \ with \ any}) = \prod_{k=1}^{M}(1-P(\mathrm{contact \ with \ circle\ } k)) \end{equation} The probability of non-contact I get differs from the simulation by quite a big margin (up to >20 times difference), and I fail to understand why?
List of checks performed
- The distance distributions agree with the simulated
- The distribution of the sum of two radii is correct
- In the simulation, I try to eliminate the boundary effects by only considering the circles with origins within some distance from the simulation borders
- Simulation is performed with relatively high $\lambda$, large $A$ and multiple repeats to ensure the variability is not the root cause for the difference
MWE python code and sample outputs
from matplotlib import pyplot as plt
from scipy.stats import poisson, norm, gengamma
from scipy.integrate import trapz
%matplotlib inline
plt.rcParams["font.size"] = 12
np.random.seed(1)
# Circle size parameters
mu = 0.02
sigma = 0.2*mu
# Circle density [1/area]
la = 600.0
A = 20.0 # Area
w = np.sqrt(A) # Width
##############
# Simulation #
##############
N = poisson.rvs(la*A) # Number of circles
xs = np.random.rand(N)*w # Simulated x-coordinates
ys = np.random.rand(N)*w # Simulated y-coordinates
# Border effect - consider only circles that are some distance away from the simulation border
val_ind = (xs >= 0.1*w) & (xs <= 0.9*w) & (ys >= 0.1*w) & (ys <= 0.9*w)
print("N = %i, N_val = %i"%(N, sum(val_ind)))
rs = np.random.randn(N)*sigma + mu # Simulated radii
Ds = np.zeros((N,N)) # Distance matrix
contact = np.zeros((N,N), dtype=bool) # Contact-indicator matrix
for i in range(N):
Ds[i,:] = np.sqrt((xs[i] - xs)**2 + (ys[i] - ys)**2) # Distances to other circle centers
contact[i,rs[i] + rs >= Ds[i,:]] = True # Determine which circles are in contact
np.fill_diagonal(contact, False) # Remove self-contact
Ds_sorted = np.sort(Ds) # Sort by distance
contact_counts = np.sum(contact, axis=0)
print("P(no contact) = %.2f%%"%(float(sum(contact_counts[val_ind]==0))/sum(val_ind)*100))
########################
# Prediction and plots #
########################
fig, ax = plt.subplots(figsize=(12.,5.))
rv_z = norm(2.0*mu, np.sqrt(2.0)*sigma) # Sum of two normally distributed radii, both with parameters (mu, sigma)
# Number of nearest neighbors to be considered
M_candidates = np.arange(1,101)
M = M_candidates[np.where(gengamma.ppf(1e-4, M_candidates, 2, scale=(la*np.pi)**(-1.0/2.0)) > rv_z.ppf(1.0 - 1e-4))[0][0]]
r = np.linspace(0.0, gengamma.ppf(1.0-1e-4, M, 2, scale=(la*np.pi)**(-1.0/2.0)), 400) # Distance / radius
# Z = R_i + R_j
fz = rv_z.pdf(r)
Fz = rv_z.cdf(r)
line, = plt.plot(r, fz, 'k-', linewidth=2)
rs2 = rs.copy()
np.random.shuffle(rs2)
zs = rs + rs2 # Simulate sum of two radii
plt.hist(zs, bins=50, density=True, color=line.get_color(), alpha=0.3)
ps = np.zeros(M) # Probabilities of being in contact with ith nearest circle
for i in range(1, M+1):
fd = gengamma.pdf(r, i, 2, scale=(la*np.pi)**(-1.0/2.0)) # Distance to ith neighbour pdf
Fd = gengamma.cdf(r, i, 2, scale=(la*np.pi)**(-1.0/2.0)) # Distance to ith neighbour cdf
line, = plt.plot(r, fd)
plt.text(r[np.argmax(fd)], 1.05*np.max(fd), str(i))
plt.hist(Ds_sorted[:,i], bins=50, density=True, alpha=0.3, color=line.get_color()) # Simulated distances to ith
ps[i-1] = trapz(Fd*fz, r) # P(contact) = P(R_0 + R_i \ge D_i)
print("i = %i, P(contact with circle %i) = %f%%"%(i, i, ps[i-1]*100))
plt.xlim([0.0, r.max()])
plt.xlabel("$r,d$")
plt.ylabel("$f(r), f(d)$")
print("P(no contact to any of the circles) = %.2f%%"%((np.prod(1.0-ps))*100))
with output prints
N = 11974, N_val = 7715
P(no contact) = 6.21%
i = 1, P(contact with circle 1) = 93.595769%
i = 2, P(contact with circle 2) = 77.871000%
i = 3, P(contact with circle 3) = 56.891694%
i = 4, P(contact with circle 4) = 36.800897%
i = 5, P(contact with circle 5) = 21.376076%
i = 6, P(contact with circle 6) = 11.307534%
i = 7, P(contact with circle 7) = 5.514789%
i = 8, P(contact with circle 8) = 2.505546%
i = 9, P(contact with circle 9) = 1.069581%
i = 10, P(contact with circle 10) = 0.432070%
i = 11, P(contact with circle 11) = 0.166151%
i = 12, P(contact with circle 12) = 0.061127%
i = 13, P(contact with circle 13) = 0.021606%
i = 14, P(contact with circle 14) = 0.007364%
i = 15, P(contact with circle 15) = 0.002428%
i = 16, P(contact with circle 16) = 0.000776%
i = 17, P(contact with circle 17) = 0.000241%
i = 18, P(contact with circle 18) = 0.000073%
i = 19, P(contact with circle 19) = 0.000022%
i = 20, P(contact with circle 20) = 0.000006%
P(no contact to any of the circles) = 0.24%
And output plot with the distribution of two radii added together as black thicker line and the distances to $k$th neighbor, both predicted (continuous lines) and simulated (histograms):
So the predicted non-contact probability is 0.24% whereas the simulated is 6.21%. So there is obviously something wrong with my analysis/simulation. The distributions shown in the plot above indicate that the predicted non-contact probability is plausible.