Spaces:
Sleeping
Sleeping
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| from scipy.stats import norm | |
| import gradio as gr | |
| import io | |
| from PIL import Image | |
| # ============================================================================ | |
| # CORE CLASSES (Same as before, but with comments for learning) | |
| # ============================================================================ | |
| class LGCP: | |
| """Log-Gaussian Cox Process model for uncertain target arrivals.""" | |
| def __init__(self, mean_func, var_func, corr_len=1.0): | |
| self.mean_func = mean_func | |
| self.var_func = var_func | |
| self.corr_len = corr_len | |
| def mean_intensity(self, s): | |
| """Compute mean intensity E[λ(s)] = exp(μ + σ²/2) for log-normal.""" | |
| mu = self.mean_func(s) | |
| sigma2 = self.var_func(s) | |
| return np.exp(mu + sigma2 / 2) | |
| def quantile_intensity(self, s, alpha): | |
| """Compute α-quantile: λ_α = exp(μ + z_α * σ).""" | |
| mu = self.mean_func(s) | |
| sigma = np.sqrt(self.var_func(s)) | |
| return np.exp(mu + norm.ppf(alpha) * sigma) | |
| def sample(self, s, n_samples=1): | |
| """Sample spatially correlated intensity functions.""" | |
| n = len(s) | |
| mu = self.mean_func(s) | |
| sigma = np.sqrt(self.var_func(s)) | |
| dist = np.abs(s.reshape(-1, 1) - s.reshape(1, -1)) | |
| corr = np.exp(-dist**2 / (2 * self.corr_len**2)) | |
| cov = np.outer(sigma, sigma) * corr + 1e-6 * np.eye(n) | |
| L = np.linalg.cholesky(cov) | |
| samples = np.zeros((n_samples, n)) | |
| for i in range(n_samples): | |
| samples[i] = np.exp(mu + L @ np.random.randn(n)) | |
| return samples | |
| class Sensors: | |
| """Gaussian detection probability model.""" | |
| def __init__(self, rho=0.95, sigma_l=0.25): | |
| self.rho = rho | |
| self.sigma_l = sigma_l | |
| def detect_prob(self, s, loc): | |
| """Detection probability γ(s, a) = ρ * exp(-(s-a)²/σ_l).""" | |
| return self.rho * np.exp(-(s - loc)**2 / self.sigma_l) | |
| def miss_prob(self, s, locs): | |
| """Probability all sensors miss: π(s, a) = ∏(1 - γ(s, a_i)).""" | |
| if len(locs) == 0: | |
| return np.ones_like(s) | |
| miss = np.ones_like(s) | |
| for loc in locs: | |
| miss *= (1 - self.detect_prob(s, loc)) | |
| return miss | |
| def greedy_optimize(grid, candidates, intensity, sensors, n_sensors): | |
| """Greedy sensor placement algorithm.""" | |
| ds = grid[1] - grid[0] | |
| selected = [] | |
| for _ in range(n_sensors): | |
| best_gain = -np.inf | |
| best_loc = None | |
| current_miss = sensors.miss_prob(grid, np.array(selected)) | |
| current_val = np.sum(intensity * (1 - current_miss)) * ds | |
| for c in candidates: | |
| if c in selected: | |
| continue | |
| new_miss = sensors.miss_prob(grid, np.array(selected + [c])) | |
| new_val = np.sum(intensity * (1 - new_miss)) * ds | |
| gain = new_val - current_val | |
| if gain > best_gain: | |
| best_gain = gain | |
| best_loc = c | |
| if best_loc is not None: | |
| selected.append(best_loc) | |
| return np.array(selected) | |
| def evaluate(grid, sensor_locs, intensity_samples, sensors): | |
| """Monte Carlo evaluation of sensor placement.""" | |
| ds = grid[1] - grid[0] | |
| miss = sensors.miss_prob(grid, sensor_locs) | |
| void_probs = [] | |
| for sample in intensity_samples: | |
| expected_missed = np.sum(sample * miss) * ds | |
| void_probs.append(np.exp(-expected_missed)) | |
| void_probs = np.array(void_probs) | |
| return { | |
| 'mean': np.mean(void_probs), | |
| 'std': np.std(void_probs), | |
| 'p5': np.percentile(void_probs, 5), | |
| 'p10': np.percentile(void_probs, 10), | |
| 'p25': np.percentile(void_probs, 25), | |
| 'median': np.median(void_probs), | |
| 'all': void_probs | |
| } | |
| # ============================================================================ | |
| # ENVIRONMENT CREATION | |
| # ============================================================================ | |
| def create_environment(hotspot1_pos, hotspot1_strength, hotspot2_pos, hotspot2_strength, | |
| uncertainty_pos, uncertainty_strength): | |
| """Create customizable environment.""" | |
| def mean_func(s): | |
| region1 = hotspot1_strength * np.exp(-((s - hotspot1_pos)**2) / 0.8) | |
| region2 = hotspot2_strength * np.exp(-((s - hotspot2_pos)**2) / 0.5) | |
| return -1.5 + region1 + region2 | |
| def var_func(s): | |
| base = 0.1 | |
| uncertain_zone = uncertainty_strength * np.exp(-((s - uncertainty_pos)**2) / 0.8) | |
| return base + uncertain_zone | |
| return mean_func, var_func | |
| # ============================================================================ | |
| # VISUALIZATION FUNCTIONS | |
| # ============================================================================ | |
| def plot_detection_formula(rho, sigma_l): | |
| """Visualize the detection probability formula.""" | |
| fig, axes = plt.subplots(1, 2, figsize=(12, 4)) | |
| # Plot 1: Detection probability curve | |
| sensor_loc = 5 | |
| s = np.linspace(0, 10, 200) | |
| sensors = Sensors(rho=rho, sigma_l=sigma_l) | |
| detect_probs = sensors.detect_prob(s, sensor_loc) | |
| ax1 = axes[0] | |
| ax1.plot(s, detect_probs, 'b-', linewidth=2) | |
| ax1.axvline(sensor_loc, color='red', linestyle='--', label=f'Sensor at {sensor_loc}') | |
| ax1.fill_between(s, 0, detect_probs, alpha=0.3) | |
| ax1.set_xlabel('Location (s)', fontsize=12) | |
| ax1.set_ylabel('Detection Probability', fontsize=12) | |
| ax1.set_title(f'Detection Probability Formula\nγ(s) = {rho} × exp(-(s-{sensor_loc})² / {sigma_l})', fontsize=11) | |
| ax1.legend() | |
| ax1.grid(True, alpha=0.3) | |
| ax1.set_ylim(0, 1) | |
| # Plot 2: Effect of parameters | |
| ax2 = axes[1] | |
| # Different sigma_l values | |
| for sl in [0.1, 0.25, 0.5, 1.0]: | |
| temp_sensors = Sensors(rho=rho, sigma_l=sl) | |
| probs = temp_sensors.detect_prob(s, sensor_loc) | |
| ax2.plot(s, probs, label=f'σₗ = {sl}', linewidth=2) | |
| ax2.axvline(sensor_loc, color='gray', linestyle='--', alpha=0.5) | |
| ax2.set_xlabel('Location (s)', fontsize=12) | |
| ax2.set_ylabel('Detection Probability', fontsize=12) | |
| ax2.set_title(f'Effect of σₗ (sensor range)\nρ = {rho} fixed', fontsize=11) | |
| ax2.legend() | |
| ax2.grid(True, alpha=0.3) | |
| ax2.set_ylim(0, 1) | |
| plt.tight_layout() | |
| return fig | |
| def plot_intensity_explanation(hotspot1_pos, hotspot1_strength, hotspot2_pos, hotspot2_strength, | |
| uncertainty_pos, uncertainty_strength): | |
| """Visualize mean intensity and variance.""" | |
| fig, axes = plt.subplots(1, 3, figsize=(14, 4)) | |
| grid = np.linspace(0, 10, 200) | |
| mean_func, var_func = create_environment( | |
| hotspot1_pos, hotspot1_strength, hotspot2_pos, hotspot2_strength, | |
| uncertainty_pos, uncertainty_strength | |
| ) | |
| lgcp = LGCP(mean_func, var_func) | |
| # Plot 1: Mean function (mu) | |
| ax1 = axes[0] | |
| mu_values = mean_func(grid) | |
| ax1.plot(grid, mu_values, 'g-', linewidth=2) | |
| ax1.fill_between(grid, mu_values.min(), mu_values, alpha=0.3, color='green') | |
| ax1.set_xlabel('Location (s)', fontsize=12) | |
| ax1.set_ylabel('μ(s)', fontsize=12) | |
| ax1.set_title('Step 1: Mean of Log-Intensity μ(s)', fontsize=11) | |
| ax1.grid(True, alpha=0.3) | |
| # Plot 2: Variance function | |
| ax2 = axes[1] | |
| var_values = var_func(grid) | |
| ax2.plot(grid, var_values, 'orange', linewidth=2) | |
| ax2.fill_between(grid, 0, var_values, alpha=0.3, color='orange') | |
| ax2.set_xlabel('Location (s)', fontsize=12) | |
| ax2.set_ylabel('σ²(s)', fontsize=12) | |
| ax2.set_title('Step 2: Variance (Uncertainty) σ²(s)', fontsize=11) | |
| ax2.grid(True, alpha=0.3) | |
| # Plot 3: Final mean intensity | |
| ax3 = axes[2] | |
| mean_int = lgcp.mean_intensity(grid) | |
| q90_int = lgcp.quantile_intensity(grid, 0.90) | |
| ax3.plot(grid, mean_int, 'b-', linewidth=2, label='Mean Intensity E[λ]') | |
| ax3.plot(grid, q90_int, 'r-', linewidth=2, label='Q90 Intensity') | |
| ax3.fill_between(grid, mean_int, q90_int, alpha=0.3, color='red') | |
| ax3.set_xlabel('Location (s)', fontsize=12) | |
| ax3.set_ylabel('Intensity', fontsize=12) | |
| ax3.set_title('Step 3: Final Intensity = exp(μ + σ²/2)', fontsize=11) | |
| ax3.legend() | |
| ax3.grid(True, alpha=0.3) | |
| plt.tight_layout() | |
| return fig | |
| def run_full_analysis(n_sensors, rho, sigma_l, hotspot1_pos, hotspot1_strength, | |
| hotspot2_pos, hotspot2_strength, uncertainty_pos, | |
| uncertainty_strength, quantile, n_samples): | |
| """Run full sensor placement analysis.""" | |
| np.random.seed(42) | |
| # Setup | |
| grid = np.linspace(0, 10, 200) | |
| candidates = np.linspace(0.5, 9.5, 45) | |
| # Create models | |
| mean_func, var_func = create_environment( | |
| hotspot1_pos, hotspot1_strength, hotspot2_pos, hotspot2_strength, | |
| uncertainty_pos, uncertainty_strength | |
| ) | |
| lgcp = LGCP(mean_func, var_func, corr_len=0.8) | |
| sensors = Sensors(rho=rho, sigma_l=sigma_l) | |
| # Compute intensity functions | |
| mean_int = lgcp.mean_intensity(grid) | |
| quantile_int = lgcp.quantile_intensity(grid, quantile) | |
| # Optimize placements | |
| s_mean = greedy_optimize(grid, candidates, mean_int, sensors, n_sensors) | |
| s_quantile = greedy_optimize(grid, candidates, quantile_int, sensors, n_sensors) | |
| # Evaluate | |
| n_samples = int(n_samples) | |
| samples = lgcp.sample(grid, n_samples) | |
| r_mean = evaluate(grid, s_mean, samples, sensors) | |
| r_quantile = evaluate(grid, s_quantile, samples, sensors) | |
| # Create visualization | |
| fig, axes = plt.subplots(2, 2, figsize=(14, 10)) | |
| # Plot 1: Intensity functions | |
| ax1 = axes[0, 0] | |
| ax1.plot(grid, mean_int, 'b-', label='Mean intensity E[λ(s)]', linewidth=2) | |
| ax1.plot(grid, quantile_int, 'r-', label=f'{int(quantile*100)}th percentile', linewidth=2) | |
| ax1.fill_between(grid, 0, var_func(grid), alpha=0.3, color='gray', label='Uncertainty σ²(s)') | |
| ax1.set_xlabel('Location s', fontsize=12) | |
| ax1.set_ylabel('Intensity', fontsize=12) | |
| ax1.set_title('Intensity Functions', fontsize=12) | |
| ax1.legend() | |
| ax1.grid(True, alpha=0.3) | |
| # Plot 2: Sensor placements | |
| ax2 = axes[0, 1] | |
| ax2.plot(grid, mean_int, 'b-', alpha=0.5, linewidth=1) | |
| ax2.plot(grid, quantile_int, 'r-', alpha=0.5, linewidth=1) | |
| ax2.scatter(s_mean, np.zeros_like(s_mean) - 0.02, c='blue', s=150, marker='^', | |
| label=f'Mean-based sensors', zorder=5) | |
| ax2.scatter(s_quantile, np.zeros_like(s_quantile) - 0.06, c='red', s=150, marker='v', | |
| label=f'Q{int(quantile*100)}-based sensors', zorder=5) | |
| ax2.axvspan(uncertainty_pos - 1, uncertainty_pos + 1, alpha=0.2, color='yellow', | |
| label='High uncertainty zone') | |
| ax2.set_xlabel('Location s', fontsize=12) | |
| ax2.set_ylabel('Intensity', fontsize=12) | |
| ax2.set_title('Sensor Placements Comparison', fontsize=12) | |
| ax2.legend() | |
| ax2.grid(True, alpha=0.3) | |
| # Plot 3: Void probability distributions | |
| ax3 = axes[1, 0] | |
| ax3.hist(r_mean['all'], bins=50, alpha=0.5, label='Mean-based', color='blue', density=True) | |
| ax3.hist(r_quantile['all'], bins=50, alpha=0.5, label=f'Q{int(quantile*100)}-based', color='red', density=True) | |
| ax3.axvline(r_mean['p5'], color='blue', linestyle='--', linewidth=2, | |
| label=f'Mean 5th %: {r_mean["p5"]:.3f}') | |
| ax3.axvline(r_quantile['p5'], color='red', linestyle='--', linewidth=2, | |
| label=f'Q{int(quantile*100)} 5th %: {r_quantile["p5"]:.3f}') | |
| ax3.set_xlabel('Void Probability (lower = better)', fontsize=12) | |
| ax3.set_ylabel('Density', fontsize=12) | |
| ax3.set_title('Distribution of Void Probabilities', fontsize=12) | |
| ax3.legend() | |
| ax3.grid(True, alpha=0.3) | |
| # Plot 4: Detection coverage | |
| ax4 = axes[1, 1] | |
| miss_mean = sensors.miss_prob(grid, s_mean) | |
| miss_quantile = sensors.miss_prob(grid, s_quantile) | |
| ax4.plot(grid, 1 - miss_mean, 'b-', label='Mean-based coverage', linewidth=2) | |
| ax4.plot(grid, 1 - miss_quantile, 'r-', label=f'Q{int(quantile*100)}-based coverage', linewidth=2) | |
| ax4.axvspan(uncertainty_pos - 1, uncertainty_pos + 1, alpha=0.2, color='yellow', | |
| label='High uncertainty zone') | |
| ax4.set_xlabel('Location s', fontsize=12) | |
| ax4.set_ylabel('Detection Probability', fontsize=12) | |
| ax4.set_title('Detection Coverage Along Border', fontsize=12) | |
| ax4.legend() | |
| ax4.grid(True, alpha=0.3) | |
| plt.tight_layout() | |
| # Generate results text | |
| improvement = (r_quantile['p5'] - r_mean['p5']) / r_mean['p5'] * 100 | |
| results_text = f""" | |
| ## 📊 RESULTS | |
| ### Sensor Locations: | |
| - **Mean-based:** {np.round(s_mean, 2).tolist()} | |
| - **Q{int(quantile*100)}-based:** {np.round(s_quantile, 2).tolist()} | |
| ### Performance Comparison: | |
| | Metric | Mean-Based | Q{int(quantile*100)}-Based | Winner | | |
| |--------|-----------|------------|--------| | |
| | VP Mean | {r_mean['mean']:.4f} | {r_quantile['mean']:.4f} | {'Q'+str(int(quantile*100)) if r_quantile['mean'] < r_mean['mean'] else 'Mean'} | | |
| | VP Median | {r_mean['median']:.4f} | {r_quantile['median']:.4f} | {'Q'+str(int(quantile*100)) if r_quantile['median'] < r_mean['median'] else 'Mean'} | | |
| | VP 10th % | {r_mean['p10']:.4f} | {r_quantile['p10']:.4f} | {'Q'+str(int(quantile*100)) if r_quantile['p10'] > r_mean['p10'] else 'Mean'} | | |
| | **VP 5th %** | **{r_mean['p5']:.4f}** | **{r_quantile['p5']:.4f}** | **{'Q'+str(int(quantile*100)) if r_quantile['p5'] > r_mean['p5'] else 'Mean'}** | | |
| ### Key Finding: | |
| **Q{int(quantile*100)} worst-case (5th percentile) improvement: {improvement:.1f}%** | |
| {'✅ Conservative approach is BETTER for worst-case!' if improvement > 0 else '⚠️ Mean-based performs better in this scenario'} | |
| """ | |
| return fig, results_text | |
| def plot_single_sensor_demo(sensor_position, rho, sigma_l): | |
| """Interactive demo of a single sensor.""" | |
| fig, ax = plt.subplots(figsize=(10, 5)) | |
| grid = np.linspace(0, 10, 200) | |
| sensors = Sensors(rho=rho, sigma_l=sigma_l) | |
| # Detection probability | |
| detect = sensors.detect_prob(grid, sensor_position) | |
| # Plot | |
| ax.fill_between(grid, 0, detect, alpha=0.3, color='blue', label='Detection zone') | |
| ax.plot(grid, detect, 'b-', linewidth=2) | |
| ax.axvline(sensor_position, color='red', linestyle='--', linewidth=2, label=f'Sensor at {sensor_position:.1f}') | |
| # Add annotations | |
| ax.annotate(f'Max detection: {rho*100:.0f}%', | |
| xy=(sensor_position, rho), xytext=(sensor_position + 1, rho + 0.1), | |
| arrowprops=dict(arrowstyle='->', color='black'), | |
| fontsize=11) | |
| ax.set_xlabel('Location along border', fontsize=12) | |
| ax.set_ylabel('Detection Probability', fontsize=12) | |
| ax.set_title(f'Single Sensor Detection Coverage\nFormula: γ(s) = {rho} × exp(-(s - {sensor_position:.1f})² / {sigma_l})', | |
| fontsize=12) | |
| ax.legend(loc='upper right') | |
| ax.grid(True, alpha=0.3) | |
| ax.set_xlim(0, 10) | |
| ax.set_ylim(0, 1.1) | |
| plt.tight_layout() | |
| return fig | |
| # ============================================================================ | |
| # GRADIO INTERFACE | |
| # ============================================================================ | |
| with gr.Blocks(title="🎯 Sensor Placement Explorer", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown(""" | |
| # 🎯 Risk-Aware Sensor Placement Explorer | |
| **Learn how to optimally place sensors to detect intruders along a border!** | |
| This interactive tool helps you understand: | |
| - How the **detection formula** works | |
| - What **mean intensity** and **variance** mean | |
| - Why **conservative (Q90) placement** can be better than **mean-based placement** | |
| --- | |
| """) | |
| with gr.Tabs(): | |
| # ===================================================================== | |
| # TAB 1: Detection Formula | |
| # ===================================================================== | |
| with gr.TabItem("1️⃣ Detection Formula"): | |
| gr.Markdown(""" | |
| ## Understanding the Detection Formula | |
| Each sensor detects intruders based on **distance**: | |
| $$\\gamma(s, a) = \\rho \\times e^{-\\frac{(s - a)^2}{\\sigma_l}}$$ | |
| - **ρ (rho)**: Maximum detection probability (e.g., 95%) | |
| - **σₗ (sigma_l)**: Sensor range (higher = wider coverage) | |
| - **s - a**: Distance between intruder and sensor | |
| **Try adjusting the sliders to see how parameters affect detection!** | |
| """) | |
| with gr.Row(): | |
| rho_slider = gr.Slider(0.5, 1.0, value=0.95, step=0.05, | |
| label="ρ (rho) - Maximum Detection Probability") | |
| sigma_l_slider = gr.Slider(0.1, 2.0, value=0.25, step=0.05, | |
| label="σₗ (sigma_l) - Sensor Range") | |
| detect_plot = gr.Plot(label="Detection Probability Visualization") | |
| detect_btn = gr.Button("🔍 Update Detection Plot", variant="primary") | |
| detect_btn.click(plot_detection_formula, [rho_slider, sigma_l_slider], detect_plot) | |
| # ===================================================================== | |
| # TAB 2: Single Sensor Demo | |
| # ===================================================================== | |
| with gr.TabItem("2️⃣ Single Sensor Demo"): | |
| gr.Markdown(""" | |
| ## Interactive Single Sensor | |
| Move the sensor along the border and see its detection coverage! | |
| The **blue shaded area** shows where the sensor can detect intruders. | |
| """) | |
| with gr.Row(): | |
| pos_slider = gr.Slider(0, 10, value=5, step=0.1, | |
| label="Sensor Position (0-10)") | |
| rho_slider2 = gr.Slider(0.5, 1.0, value=0.95, step=0.05, | |
| label="ρ (rho) - Max Detection") | |
| sigma_l_slider2 = gr.Slider(0.1, 2.0, value=0.25, step=0.05, | |
| label="σₗ (sigma_l) - Range") | |
| single_plot = gr.Plot(label="Single Sensor Coverage") | |
| single_btn = gr.Button("🎯 Update Sensor", variant="primary") | |
| single_btn.click(plot_single_sensor_demo, | |
| [pos_slider, rho_slider2, sigma_l_slider2], | |
| single_plot) | |
| # ===================================================================== | |
| # TAB 3: Intensity Explanation | |
| # ===================================================================== | |
| with gr.TabItem("3️⃣ Intensity & Uncertainty"): | |
| gr.Markdown(""" | |
| ## Understanding Intensity and Uncertainty | |
| **Intensity** = How many intruders expected at each location (heat map) | |
| **Variance/Uncertainty** = How unsure we are about the estimate | |
| The formula: **Mean Intensity = exp(μ + σ²/2)** | |
| - Higher variance → higher mean intensity (because extreme values pull average up) | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("### Hotspot 1 (Left region)") | |
| h1_pos = gr.Slider(0, 10, value=2.5, step=0.5, label="Position") | |
| h1_str = gr.Slider(0, 1, value=0.3, step=0.1, label="Strength") | |
| with gr.Column(): | |
| gr.Markdown("### Hotspot 2 (Right region)") | |
| h2_pos = gr.Slider(0, 10, value=7.5, step=0.5, label="Position") | |
| h2_str = gr.Slider(0, 1, value=0.2, step=0.1, label="Strength") | |
| with gr.Column(): | |
| gr.Markdown("### Uncertainty Zone") | |
| u_pos = gr.Slider(0, 10, value=5, step=0.5, label="Position") | |
| u_str = gr.Slider(0, 4, value=2.0, step=0.2, label="Strength") | |
| intensity_plot = gr.Plot(label="Intensity Visualization") | |
| intensity_btn = gr.Button("📊 Update Intensity Plot", variant="primary") | |
| intensity_btn.click(plot_intensity_explanation, | |
| [h1_pos, h1_str, h2_pos, h2_str, u_pos, u_str], | |
| intensity_plot) | |
| # ===================================================================== | |
| # TAB 4: Full Analysis | |
| # ===================================================================== | |
| with gr.TabItem("4️⃣ Full Analysis"): | |
| gr.Markdown(""" | |
| ## Complete Sensor Placement Analysis | |
| Compare **Mean-based** vs **Conservative (Quantile-based)** sensor placement! | |
| - **Mean-based**: Optimizes for average conditions | |
| - **Quantile-based**: Prepares for worse-than-expected scenarios | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("### Sensor Settings") | |
| n_sensors = gr.Slider(2, 10, value=6, step=1, label="Number of Sensors") | |
| rho_full = gr.Slider(0.5, 1.0, value=0.95, step=0.05, label="ρ (Max Detection)") | |
| sigma_l_full = gr.Slider(0.1, 2.0, value=0.25, step=0.05, label="σₗ (Sensor Range)") | |
| with gr.Column(): | |
| gr.Markdown("### Environment Settings") | |
| h1_pos_full = gr.Slider(0, 10, value=2.5, step=0.5, label="Hotspot 1 Position") | |
| h1_str_full = gr.Slider(0, 1, value=0.3, step=0.1, label="Hotspot 1 Strength") | |
| h2_pos_full = gr.Slider(0, 10, value=7.5, step=0.5, label="Hotspot 2 Position") | |
| h2_str_full = gr.Slider(0, 1, value=0.2, step=0.1, label="Hotspot 2 Strength") | |
| with gr.Column(): | |
| gr.Markdown("### Uncertainty & Analysis") | |
| u_pos_full = gr.Slider(0, 10, value=5, step=0.5, label="Uncertainty Zone Position") | |
| u_str_full = gr.Slider(0, 4, value=2.0, step=0.2, label="Uncertainty Strength") | |
| quantile = gr.Slider(0.7, 0.99, value=0.90, step=0.01, label="Quantile (e.g., 0.90 = Q90)") | |
| n_samples = gr.Slider(500, 5000, value=2000, step=500, label="Monte Carlo Samples") | |
| full_plot = gr.Plot(label="Analysis Results") | |
| results_md = gr.Markdown("*Click 'Run Full Analysis' to see results*") | |
| full_btn = gr.Button("🚀 Run Full Analysis", variant="primary", size="lg") | |
| full_btn.click(run_full_analysis, | |
| [n_sensors, rho_full, sigma_l_full, h1_pos_full, h1_str_full, | |
| h2_pos_full, h2_str_full, u_pos_full, u_str_full, quantile, n_samples], | |
| [full_plot, results_md]) | |
| # ===================================================================== | |
| # TAB 5: Learning Summary | |
| # ===================================================================== | |
| with gr.TabItem("📚 Learning Summary"): | |
| gr.Markdown(""" | |
| ## Key Concepts Summary | |
| ### 1️⃣ Detection Formula | |
| ``` | |
| γ(s, a) = ρ × exp(-(s - a)² / σₗ) | |
| ``` | |
| - **ρ**: Max detection probability (0.95 = 95%) | |
| - **σₗ**: How far the sensor can "see" | |
| - **The negative sign**: Makes probability DECREASE with distance | |
| --- | |
| ### 2️⃣ Mean Intensity Formula | |
| ``` | |
| E[λ(s)] = exp(μ + σ²/2) | |
| ``` | |
| - **μ**: Average of log-intensity | |
| - **σ²**: Variance (uncertainty) | |
| - **Why σ²/2?**: Corrects for the fact that exp() shifts the average up | |
| --- | |
| ### 3️⃣ Quantile Intensity (Conservative) | |
| ``` | |
| λ_α(s) = exp(μ + z_α × σ) | |
| ``` | |
| - **z_α**: The z-score for percentile α (e.g., z₀.₉₀ ≈ 1.28) | |
| - **Higher quantile = more conservative** | |
| --- | |
| ### 4️⃣ Greedy Algorithm | |
| 1. Start with no sensors | |
| 2. Try each possible location | |
| 3. Pick the one with BIGGEST improvement | |
| 4. Repeat until all sensors placed | |
| --- | |
| ### 5️⃣ When to Use Each Approach | |
| | Situation | Approach | Why | | |
| |-----------|----------|-----| | |
| | Wildlife monitoring | Mean-based | Low stakes, average is fine | | |
| | Border security | Q90-based | High stakes, need worst-case protection | | |
| | Nuclear facility | Q95+ based | Critical, can't afford to miss | | |
| --- | |
| ### 6️⃣ Key Insight | |
| **Conservative placement (Q90) may sacrifice ~1% average performance | |
| but gains 10-15% improvement in worst-case scenarios!** | |
| For high-stakes applications, this trade-off is almost always worth it. | |
| """) | |
| gr.Markdown(""" | |
| --- | |
| ### 🔗 How to Use This App | |
| 1. **Start with Tab 1**: Understand how the detection formula works | |
| 2. **Try Tab 2**: Move a single sensor around and see its coverage | |
| 3. **Explore Tab 3**: See how intensity and uncertainty are calculated | |
| 4. **Run Tab 4**: Do a full analysis comparing Mean vs Conservative placement | |
| 5. **Review Tab 5**: Summarize what you've learned! | |
| --- | |
| *Created for learning sensor placement optimization with Log-Gaussian Cox Process models* | |
| """) | |
| # Launch the app | |
| if __name__ == "__main__": | |
| demo.launch() |