Domain: Math — Probability theory and stochastic processes

Problem:
Let \(X_i \sim \mathrm{Gamma}(\alpha_i, \beta_i)\) for \(i = 1, \dots, 20\) be independent random variables with integer shape parameters \(\alpha_i \in \mathbb{N}\) and distinct scale parameters \(\beta_i > 0\). Define \(Y = \sum_{i=1}^{20} X_i\). For the given values \( (\alpha_1, \alpha_2, \alpha_3, \ldots, \alpha_{20}) = (7,\,6,\,5,\,4,\,5,\,3,\,6,\,2,\,8,\,3,\,4,\,5,\,2,\,7,\,3,\,6,\,4,\,5,\,2,\,6) \) and \( (\beta_1, \beta_2, \beta_3, \ldots, \beta_{20}) = (0.0031,\,0.0064,\,0.0150,\,0.0330,\,0.0450,\,0.0451,\,0.0452,\,0.1199,\,0.1200,\,0.1201,\,0.5200,\,0.7100,\,0.9000,\,0.9001,\,0.9002,\,0.9003,\,1.1300,\,1.7000,\,2.0500,\,3.2000) \), evaluate the exact probability density function of \(Y\) at \(y =24.375\), that is, compute \(f_Y(24.375)\) accurate to eight decimal places. Provide a final answer and a single self-contained Python code block that computes it.

Answer: 0.00000033

Solution code:
import numpy as np
from scipy import integrate

def gamma_sum_cf(t, alphas, betas):
    
    t = np.asarray(t, dtype=np.float64)
    alphas = np.asarray(alphas, dtype=np.float64)
    betas = np.asarray(betas, dtype=np.float64)
    z = 1.0 - 1j * np.outer(t, betas)  
    log_z = np.log(z)                  
    log_phi = (-alphas * log_z).sum(axis=1)
    phi = np.exp(log_phi)
    return phi

def gamma_sum_pdf_cf(y, alphas, betas,
                     chunk_size=5.0,
                     tol=1e-8,
                     max_T=500.0):
    
    alphas = np.asarray(alphas, dtype=np.float64)
    betas = np.asarray(betas, dtype=np.float64)

    def integrand(t):
        
        if t == 0.0:
          
            return 1.0
        phi_t = gamma_sum_cf(np.array([t]), alphas, betas)[0]
        return np.real(np.exp(-1j * t * y) * phi_t)

    total = 0.0
    T = 0.0
    k = 0
   
    while T < max_T:
        a = k * chunk_size
        b = (k + 1) * chunk_size
       
        val, err = integrate.quad(
            integrand, a, b,
            epsabs=tol / 10.0,
            epsrel=tol / 10.0,
            limit=200
        )
        total += val

        phi_b = gamma_sum_cf(np.array([b]), alphas, betas)[0]
        chunk_abs = abs(val)
        cf_abs = abs(phi_b)

        if chunk_abs < tol and cf_abs < 1e-4:
            break

        k += 1
        T = b

    return total / np.pi

if __name__ == "__main__":
   
    alphas = [7, 6, 5, 4, 5, 3, 6, 2, 8, 3, 4, 5, 2, 7, 3, 6, 4, 5, 2, 6]
    betas  = [
        0.0031, 0.0064, 0.0150, 0.0330, 0.0450,
        0.0451, 0.0452, 0.1199, 0.1200, 0.1201,
        0.5200, 0.7100, 0.9000, 0.9001, 0.9002,
        0.9003, 1.1300, 1.7000, 2.0500, 3.2000
    ]

    y_val = 24.375

    f_val = gamma_sum_pdf_cf(y_val, alphas, betas,
                             chunk_size=5.0,
                             tol=1e-8,
                             max_T=500.0)


    print(f"\nf_Y({y_val}) ≈ {f_val:.8f}")