An explanation for drift in diffusion processes in terms of probability distributions?

Consider the following Python implementation of an empirical experiment involving a diffusion processes.

from random import Random
random_source = Random()
random_source.seed(f'Arbitrary Seed')
def gauss_val(sigma: float) -> float:
    val = random_source.gauss(1.0, sigma)
    return val
def uniform_val(max_delta: float) -> float:
    val = random_source.uniform(1.0 - max_delta, 1.0 + max_delta)
    return val
def curious_val(sigma: float) -> float:
    random_val = random_source.gauss(0.0, sigma)
    assert -0.9 < random_val < 0.9
    val = (1.0 + random_val) / (1.0 - random_val)
    return val
def diffusion_process(multiplier_value_fun, diffusion_len=100):
    result = 100.0
    for _ in range(diffusion_len):
        result *= multiplier_value_fun()
    return result
def empirical_mean(diffusion_process_fun, number_samples=10000):
    return sum(diffusion_process_fun() for _ in range(number_samples)) / number_samples
def repeat_test(empirical_mean_fun, repeats=64):
    results = [empirical_mean_fun() for _ in range(repeats)]
    print(f'Mean:{sum(results) / repeats:.2f} Min:{min(results):.2f} Max:{max(results):.2f}')

repeat_test(lambda: empirical_mean(lambda: diffusion_process(lambda: gauss_val(0.001))))
# Mean:100.00 Min:99.98 Max:100.02 => Matches intuition
repeat_test(lambda: empirical_mean(lambda: diffusion_process(lambda: gauss_val(0.01))))
# Mean:100.01 Min:99.78 Max:100.19 => Matches intuition
repeat_test(lambda: empirical_mean(lambda: diffusion_process(lambda: gauss_val(0.1))))
# Mean:99.86 Min:97.18 Max:102.89 => Matches intuition
repeat_test(lambda: empirical_mean(lambda: diffusion_process(lambda: uniform_val(0.001))))
# Mean:100.00 Min:99.99 Max:100.01 => Matches intuition
repeat_test(lambda: empirical_mean(lambda: diffusion_process(lambda: uniform_val(0.01))))
# Mean:99.99 Min:99.85 Max:100.11 => Matches intuition
repeat_test(lambda: empirical_mean(lambda: diffusion_process(lambda: uniform_val(0.1))))
# Mean:100.00 Min:98.40 Max:101.07 =>  Matches intuition
repeat_test(lambda: empirical_mean(lambda: diffusion_process(lambda: uniform_val(0.5))))
# Mean:107.62 Min:64.10 Max:334.16 =>  Matches intuition
repeat_test(lambda: empirical_mean(lambda: diffusion_process(lambda: curious_val(0.001))))
# Mean:100.02 Min:99.97 Max:100.07 =>  Contradicts intuition?
repeat_test(lambda: empirical_mean(lambda: diffusion_process(lambda: curious_val(0.01))))
# Mean:102.01 Min:101.64 Max:102.52 =>  Contradicts intuition.
repeat_test(lambda: empirical_mean(lambda: diffusion_process(lambda: curious_val(0.1))))
# Mean:768.08 Min:673.00 Max:943.07 =>  Contradicts intuition.

The results using gauss_val and uniform_val are in line with expectations. The mean is approximately 100 – with results both smaller and larger than this. The intuitive explanation is that the distributions are symmetric around "* 1.0" so the probability of growth, at each step in the diffusion, is similar to the probability of contraction.

The results using curious_val are much less intuitive. Just as with gauss_val, curious_val is centred on 1.0. The uniform_val and gauss_val experiments are intended to correspond to diffusion steps where the existing value grows, or contracts, by a small proportion. The curious_val experiment noted that it seemed arbitrary to model the random distribution of growth/contraction relative to the existing value (looking forward in time) rather than from the next value (looking backward in time). For curious_val, it is assumed that between two successive diffusion values, A and B, there is a ‘hidden’ M value… with M relating to A and B such that A*(1-x)=B*(1+x)=M. Therefore x is the percentage, relative to A that M is smaller – and also the percentage by which M was larger than B (switching “smaller” and “larger” for negative x.) I have noticed the symmetry where, if x is normally distributed with 0 mean, that the distribution defined by (1-x)/(1+x) should be indistinguishable from (1+x)/(1-x)… which strengthens the intuitive expectation that there should be no mean drift.

Please tell me if there is a silly error in my experiment. If not, I would like to establish a good explanation for why the diffusion process for curious_val consistently drifts higher. Should I be able to (analytically) calculate the (mean) rate of drift by the diffusion process for curious_val? What (if any) relationship exists between this experiment and the Cauchy distribution?


Solution 1:

Note that curious_val has a rather esoteric distribution and does not have mean 1.

By some algebra, $\frac{1-x}{1+x}= \frac{2}{1+x} - 1$. curious_val then has distribution $2Y - 1$ where Y has the reciprocal distribution of a $\mathcal{N}(1, \sigma^2)$ random variable. Y has mean $\mu=\frac{\sqrt 2}{\sigma}F(1/\sqrt 2\sigma)$ where $F$ is Dawson's function.

For $\sigma=0.1$ this works out to $\mu \approx 1.0103$, so the mean of curious_val is $\approx 1.0206$. Taking $1.0206^{100}\approx 7.68$, which agrees with your simulation.