|
|
import gradio as gr
|
|
|
import numpy as np
|
|
|
from PIL import Image
|
|
|
import heapq
|
|
|
|
|
|
def dijkstra_pathfinding(obstacles, speeds, source, target):
|
|
|
"""Simple Dijkstra pathfinding to demonstrate the algorithm"""
|
|
|
H, W = obstacles.shape
|
|
|
sx, sy = source
|
|
|
tx, ty = target
|
|
|
|
|
|
|
|
|
dist = np.full((H, W), np.inf)
|
|
|
dist[sy, sx] = 0.0
|
|
|
|
|
|
|
|
|
parent = {}
|
|
|
|
|
|
|
|
|
pq = [(0.0, sx, sy)]
|
|
|
visited = set()
|
|
|
|
|
|
|
|
|
neighbors = [(-1, 0), (1, 0), (0, -1), (0, 1)]
|
|
|
|
|
|
while pq:
|
|
|
d, x, y = heapq.heappop(pq)
|
|
|
|
|
|
if (x, y) in visited:
|
|
|
continue
|
|
|
|
|
|
visited.add((x, y))
|
|
|
|
|
|
if (x, y) == (tx, ty):
|
|
|
break
|
|
|
|
|
|
for dx, dy in neighbors:
|
|
|
nx, ny = x + dx, y + dy
|
|
|
|
|
|
if 0 <= nx < W and 0 <= ny < H:
|
|
|
if obstacles[ny, nx] > 0.5:
|
|
|
continue
|
|
|
|
|
|
|
|
|
step_speed = max(speeds[ny, nx], 0.1)
|
|
|
cost = 1.0 / step_speed
|
|
|
new_dist = d + cost
|
|
|
|
|
|
if new_dist < dist[ny, nx]:
|
|
|
dist[ny, nx] = new_dist
|
|
|
parent[(nx, ny)] = (x, y)
|
|
|
heapq.heappush(pq, (new_dist, nx, ny))
|
|
|
|
|
|
|
|
|
if (tx, ty) not in parent and (tx, ty) != (sx, sy):
|
|
|
return []
|
|
|
|
|
|
path = []
|
|
|
current = (tx, ty)
|
|
|
|
|
|
while current != (sx, sy):
|
|
|
path.append(current)
|
|
|
if current not in parent:
|
|
|
break
|
|
|
current = parent[current]
|
|
|
|
|
|
path.append((sx, sy))
|
|
|
path.reverse()
|
|
|
|
|
|
return path
|
|
|
|
|
|
|
|
|
def generate_pathfinding_demo(grid_size, obstacle_density, source_x, source_y, target_x, target_y):
|
|
|
"""Generate pathfinding visualization with actual path calculation"""
|
|
|
|
|
|
|
|
|
np.random.seed(42)
|
|
|
obstacles = np.random.random((grid_size, grid_size)) < (obstacle_density / 100)
|
|
|
obstacles = obstacles.astype(np.float32)
|
|
|
|
|
|
|
|
|
obstacles[source_y, source_x] = 0.0
|
|
|
obstacles[target_y, target_x] = 0.0
|
|
|
|
|
|
|
|
|
speeds = np.ones((grid_size, grid_size), dtype=np.float32)
|
|
|
speeds[obstacles > 0.5] = 0.0
|
|
|
|
|
|
|
|
|
path = dijkstra_pathfinding(obstacles, speeds, (source_x, source_y), (target_x, target_y))
|
|
|
|
|
|
|
|
|
vis = np.zeros((grid_size, grid_size, 3), dtype=np.uint8)
|
|
|
|
|
|
|
|
|
vis[obstacles > 0.5] = [40, 40, 40]
|
|
|
vis[obstacles < 0.5] = [200, 200, 200]
|
|
|
|
|
|
|
|
|
if path:
|
|
|
for px, py in path:
|
|
|
if 0 <= px < grid_size and 0 <= py < grid_size:
|
|
|
vis[py, px] = [0, 255, 0]
|
|
|
|
|
|
|
|
|
vis[max(0, source_y-2):min(grid_size, source_y+3),
|
|
|
max(0, source_x-2):min(grid_size, source_x+3)] = [255, 0, 0]
|
|
|
|
|
|
|
|
|
vis[max(0, target_y-2):min(grid_size, target_y+3),
|
|
|
max(0, target_x-2):min(grid_size, target_x+3)] = [0, 100, 255]
|
|
|
|
|
|
|
|
|
blocked = int(np.sum(obstacles > 0.5))
|
|
|
free = grid_size * grid_size - blocked
|
|
|
straight_dist = np.hypot(target_x - source_x, target_y - source_y)
|
|
|
path_length = len(path)
|
|
|
path_ratio = path_length / max(straight_dist, 1.0) if path else 0.0
|
|
|
|
|
|
stats = f"""
|
|
|
### Results
|
|
|
|
|
|
- **Grid Size**: {grid_size}Γ{grid_size} ({grid_size*grid_size:,} cells)
|
|
|
- **Obstacles**: {blocked:,} ({100*blocked/(grid_size*grid_size):.1f}%)
|
|
|
- **Free Cells**: {free:,} ({100*free/(grid_size*grid_size):.1f}%)
|
|
|
- **Straight Distance**: {straight_dist:.1f} cells
|
|
|
- **Path Found**: {"β
Yes" if path else "β No path exists"}
|
|
|
- **Path Length**: {path_length} cells
|
|
|
- **Path Ratio**: {path_ratio:.2f}x (efficiency)
|
|
|
|
|
|
### GPU Solver Performance
|
|
|
|
|
|
The full Optical Neuromorphic GPU solver achieves:
|
|
|
- **30-300Γ speedup** vs this CPU Dijkstra
|
|
|
- **Sub-1% accuracy** (0.64% mean error)
|
|
|
- **Near-optimal paths** (1.025Γ optimal length)
|
|
|
- **Real-time** (2-4ms per query on 512Γ512 grids)
|
|
|
|
|
|
**Colors**: π΄ Red=Start, π΅ Blue=Goal, π’ Green=Path, β¬ Black=Obstacles
|
|
|
|
|
|
This demo uses basic CPU Dijkstra. The full GPU implementation is 30-300Γ faster!
|
|
|
"""
|
|
|
|
|
|
img = Image.fromarray(vis)
|
|
|
return img, stats
|
|
|
|
|
|
|
|
|
with gr.Blocks(title="Optical Neuromorphic Eikonal Solver Demo", theme=gr.themes.Soft()) as demo:
|
|
|
gr.Markdown("""
|
|
|
# π Optical Neuromorphic Eikonal Solver
|
|
|
|
|
|
**GPU-accelerated pathfinding with 30-300Γ speedup!**
|
|
|
|
|
|
This demo shows pathfinding with basic CPU Dijkstra for comparison.
|
|
|
The full GPU solver is 30-300Γ faster using neuromorphic computing principles.
|
|
|
|
|
|
π **Full implementation**: [GitHub](https://github.com/Agnuxo1/optical-neuromorphic-eikonal-solver)
|
|
|
""")
|
|
|
|
|
|
with gr.Row():
|
|
|
with gr.Column():
|
|
|
gr.Markdown("### βοΈ Configuration")
|
|
|
grid_size = gr.Slider(64, 512, step=64, value=256, label="Grid Size")
|
|
|
obstacle_density = gr.Slider(0, 50, step=5, value=20, label="Obstacle Density (%)")
|
|
|
|
|
|
gr.Markdown("### π Source (Red)")
|
|
|
with gr.Row():
|
|
|
source_x = gr.Slider(0, 255, step=1, value=10, label="Source X")
|
|
|
source_y = gr.Slider(0, 255, step=1, value=10, label="Source Y")
|
|
|
|
|
|
gr.Markdown("### π― Target (Blue)")
|
|
|
with gr.Row():
|
|
|
target_x = gr.Slider(0, 255, step=1, value=245, label="Target X")
|
|
|
target_y = gr.Slider(0, 255, step=1, value=245, label="Target Y")
|
|
|
|
|
|
solve_btn = gr.Button("π― Find Path", variant="primary", size="lg")
|
|
|
|
|
|
with gr.Column():
|
|
|
gr.Markdown("### πΊοΈ Pathfinding Visualization")
|
|
|
output_img = gr.Image(label="Grid with Optimal Path", height=400)
|
|
|
stats_text = gr.Markdown("Click 'Find Path' to calculate optimal route")
|
|
|
|
|
|
def update_sliders(size):
|
|
|
return [gr.update(maximum=size-1)] * 4
|
|
|
|
|
|
grid_size.change(fn=update_sliders, inputs=[grid_size],
|
|
|
outputs=[source_x, source_y, target_x, target_y])
|
|
|
|
|
|
solve_btn.click(
|
|
|
fn=generate_pathfinding_demo,
|
|
|
inputs=[grid_size, obstacle_density, source_x, source_y, target_x, target_y],
|
|
|
outputs=[output_img, stats_text]
|
|
|
)
|
|
|
|
|
|
gr.Markdown("""
|
|
|
---
|
|
|
|
|
|
## π Resources & Links
|
|
|
|
|
|
- **Paper (HTML)**: [Full Academic Paper](https://github.com/Agnuxo1/optical-neuromorphic-eikonal-solver/blob/main/optical_neuromorphic_paper.html)
|
|
|
- **Source Code**: [GitHub Repository](https://github.com/Agnuxo1/optical-neuromorphic-eikonal-solver)
|
|
|
- **Datasets**: [HuggingFace](https://huggingface.co/datasets/Agnuxo/optical-neuromorphic-eikonal-benchmarks)
|
|
|
- **OpenML**: [Datasets #47114-47118](https://www.openml.org/search?type=data&sort=date&status=active)
|
|
|
- **W&B Metrics**: [Weights & Biases](https://wandb.ai/lareliquia-angulo-agnuxo/optical-neuromorphic-eikonal-solver)
|
|
|
|
|
|
## π Benchmark Results
|
|
|
|
|
|
| Grid Size | GPU Time | CPU Time | Speedup | Accuracy | Path Quality |
|
|
|
|-----------|----------|----------|---------|----------|--------------|
|
|
|
| 128Γ128 | 2.3 ms | 73.9 ms | **32Γ** | 0.52% error | 1.004Γ optimal |
|
|
|
| 256Γ256 | 3.1 ms | 275.5 ms | **89Γ** | 0.55% error | 1.015Γ optimal |
|
|
|
| 512Γ512 | 4.1 ms | 948.9 ms | **231Γ** | 0.69% error | 1.042Γ optimal |
|
|
|
| **Average** | **3.3 ms** | **505 ms** | **135Γ** | **0.64% error** | **1.025Γ optimal** |
|
|
|
|
|
|
## π§ How It Works
|
|
|
|
|
|
The GPU solver uses **neuromorphic computing** principles:
|
|
|
- Each grid cell maintains 4 directional memory states (N, E, S, W)
|
|
|
- Information propagates like waves through the medium
|
|
|
- All cells update in parallel on GPU (massively parallel)
|
|
|
- System converges to optimal solution in ~2n iterations
|
|
|
- No priority queues β Full parallelization
|
|
|
|
|
|
## π€ Author
|
|
|
|
|
|
**Francisco Angulo de Lafuente**
|
|
|
|
|
|
- [GitHub](https://github.com/Agnuxo1)
|
|
|
- [ResearchGate](https://www.researchgate.net/profile/Francisco-Angulo-Lafuente-3)
|
|
|
- [Kaggle](https://www.kaggle.com/franciscoangulo)
|
|
|
- [HuggingFace](https://huggingface.co/Agnuxo)
|
|
|
- [Wikipedia](https://es.wikipedia.org/wiki/Francisco_Angulo_de_Lafuente)
|
|
|
|
|
|
## π Citation
|
|
|
|
|
|
If you use this work, please cite:
|
|
|
|
|
|
```bibtex
|
|
|
@software{angulo2025optical,
|
|
|
author = {Angulo de Lafuente, Francisco},
|
|
|
title = {Optical Neuromorphic Eikonal Solver},
|
|
|
year = {2025},
|
|
|
url = {https://github.com/Agnuxo1/optical-neuromorphic-eikonal-solver}
|
|
|
}
|
|
|
```
|
|
|
""")
|
|
|
|
|
|
demo.launch()
|
|
|
|
|
|
|