1+ # Setting the path for XLuminA modules:
2+ import os
3+ import sys
4+ current_path = os .path .abspath (os .path .join ('..' ))
5+ dir_path = os .path .dirname (current_path )
6+ module_path = os .path .join (dir_path )
7+ if module_path not in sys .path :
8+ sys .path .append (module_path )
9+
10+ from xlumina .__init__ import um , nm , cm , mm
11+ from xlumina .vectorized_optics import *
12+ from xlumina .optical_elements import hybrid_setup_fixed_slms_fluorophores , hybrid_setup_fixed_slms
13+ from xlumina .loss_functions import vectorized_loss_hybrid
14+ from xlumina .toolbox import space , softmin
15+ import jax .numpy as jnp
16+
17+ """
18+ Large-scale setup using fixed phase masks in random positions:
19+
20+ 3x3 initial setup - light gets detected across 6 detectors.
21+
22+ This script is valid for rediscovering
23+
24+ (1) Dorn, Quabis and Leuchs (2004) - use hybrid_setup_fixed_slms() in the loss function,
25+
26+ (2) STED microscopy - use hybrid_setup_fixed_slms_fluorophores() in the loss function.
27+ """
28+
29+ # 1. System specs:
30+ sensor_lateral_size = 824 # Resolution
31+ wavelength_1 = 632.8 * nm
32+ wavelength_2 = 530 * nm
33+ x_total = 2500 * um
34+ x , y = space (x_total , sensor_lateral_size )
35+ shape = jnp .shape (x )[0 ]
36+
37+ # 2. Define the optical functions: two orthogonally polarized beams:
38+ w0 = (1200 * um , 1200 * um )
39+ ls1 = PolarizedLightSource (x , y , wavelength_1 )
40+ ls1 .gaussian_beam (w0 = w0 , jones_vector = (1 , 1 ))
41+ ls2 = PolarizedLightSource (x , y , wavelength_2 )
42+ ls2 .gaussian_beam (w0 = w0 , jones_vector = (1 , 1 ))
43+
44+ # 3. Define the output (High Resolution) detection:
45+ x_out , y_out = jnp .array (space (10 * um , 400 ))
46+ X , Y = jnp .meshgrid (x ,y )
47+
48+ # 4. High NA objective lens specs:
49+ NA = 0.9
50+ radius_lens = 3.6 * mm / 2
51+ f_lens = radius_lens / NA
52+
53+ # 4.1 Fixed phase masks:
54+ # Polarization converter in Dorn, Quabis, Leuchs (2004):
55+ pi_half = (jnp .pi - jnp .pi / 2 ) * jnp .ones (shape = (sensor_lateral_size // 2 , sensor_lateral_size // 2 ))
56+ minus_pi_half = - jnp .pi / 2 * jnp .ones (shape = (sensor_lateral_size // 2 , sensor_lateral_size // 2 ))
57+ PM1_1 = jnp .concatenate ((jnp .concatenate ((minus_pi_half , pi_half ), axis = 1 ), jnp .concatenate ((minus_pi_half , pi_half ), axis = 1 )), axis = 0 )
58+ PM1_2 = jnp .concatenate ((jnp .concatenate ((minus_pi_half , minus_pi_half ), axis = 1 ), jnp .concatenate ((pi_half , pi_half ), axis = 1 )), axis = 0 )
59+ # Spiral phase (STED microscopy)
60+ PM2 = jnp .arctan2 (Y ,X )
61+ # Forked grating
62+ PM3 = jnp .cos (2 * PM2 - 2 * jnp .pi * X / 1000 ) * jnp .pi
63+ # Linear grating
64+ PM4_1 = jnp .sin (2 * jnp .pi * Y / 1000 ) * jnp .pi
65+ PM4_2 = jnp .sin (2 * jnp .pi * X / 1000 ) * jnp .pi
66+
67+ # 5. Static parameters - don't change during optimization:
68+ fixed_params = [radius_lens , f_lens , x_out , y_out , PM1_1 , PM1_2 , PM2 , PM3 , PM4_1 , PM4_2 ]
69+
70+ # 6. Define the loss function:
71+ def loss_hybrid_fixed_PM (parameters ):
72+ # Output from hybrid_setup is jnp.array(6, N, N): for 6 detectors
73+
74+ # Use (1) for Dorn, Quabis and Leuchs benchmark / Use (2) for STED microscopy benchmark
75+
76+ # (1):
77+ # detected_z_intensities, _ = hybrid_setup_fixed_slms(ls1, ls1, ls1, ls1, ls1, ls1, parameters, fixed_params)
78+
79+ # (2):
80+ i_effective = hybrid_setup_fixed_slms_fluorophores (ls1 , ls2 , ls1 , ls2 , ls1 , ls2 , parameters , fixed_params )
81+
82+ # Get the minimum value within loss value array of shape (6, 1, 1)
83+ loss_val = softmin (vectorized_loss_hybrid (i_effective ))
84+
85+ return loss_val
0 commit comments