The traps that wouldn’t equalize
Two symmetric optical traps. Same target intensity. The Gerchberg-Saxton algorithm running for 100 iterations. And yet one trap was bright, the other dim, oscillating back and forth forever without converging.
The problem
The standard weight update rule from Di Leonardo et al. (2007) is aggressive:
w_j(n+1) = w_j(n) × ⟨|V|⟩ / |V_j(n)|
If trap j is too bright → weight decreases. Too dim → weight increases.
The issue: with only two traps, the correction overshoots. Trap A gets too much weight, trap B too little. Next iteration, the opposite happens. The uniformity metric oscillates around 0.85 without improving.
The fix
A damping exponent γ ∈ (0, 1] that controls the correction rate:
w_j(n+1) = w_j(n) × (⟨|V|⟩ / |V_j(n)|)γ
γ=1.0 → original (aggressive, oscillates)
γ=0.5 → half-strength corrections (stable, monotonic convergence)
With γ=0.5, uniformity converges monotonically from ~0.85 to ~0.97 over 30+ iterations instead of oscillating. The key insight: making half-strength corrections each step prevents the overshoot-undershoot cycle.
The phase_scale sweet spot
Also found the right value for the phase_scale parameter. Three regimes:
| phase_scale | Fringes | Result |
|---|---|---|
| π | < 1 | Mask looks flat, no visible pattern |
| 10 × 2π ≈ 63 | 8-15 | Clear, beautiful blazed grating patterns |
| 2π × N/4 ≈ 800 | 100+ | Mask looks like noise, unresolvable |
The sweet spot at 10 × 2π gives enough spatial frequency to see the holographic structure while keeping patterns visually interpretable. Also tightened tolerance to 10⁻⁸ and enforced minimum 30 iterations before checking convergence.
Small changes, big difference. Three numbers: γ=0.5, phase_scale=10×2π, min_iterations=30. Optical tweezers on GitHub.
