@@ -11,9 +11,8 @@ Before starting, make sure TorchOptics is installed (:ref:`installation`).
1111Overview
1212--------
1313
14- TorchOptics simulates optical systems using `Fourier optics
15- <https://en.wikipedia.org/wiki/Fourier_optics> `_, where light is modeled as complex-valued
16- wavefronts sampled on 2D grids. Built on `PyTorch <https://pytorch.org/ >`_, every operation is
14+ TorchOptics simulates optical systems using Fourier optics, where light is modeled as complex-valued
15+ wavefronts sampled on 2D grids. Built on PyTorch, every operation is
1716fully differentiable, enabling gradient-based optimization of optical designs.
1817
1918The library is built around three core abstractions:
@@ -26,7 +25,7 @@ The library is built around three core abstractions:
2625- :class: `~torchoptics.System ` — An ordered sequence of elements forming a complete optical setup,
2726 analogous to :class: `torch.nn.Sequential `.
2827
29- Two global defaults control the simulation grid :
28+ Two global defaults set the physical scale of the simulation :
3029
3130- **Spacing ** — Physical distance between adjacent grid points (meters).
3231- **Wavelength ** — Wavelength of the monochromatic light (meters).
@@ -47,7 +46,7 @@ subsequent fields and elements will inherit:
4746 from torchoptics.elements import AmplitudeModulator, Lens
4847 from torchoptics.profiles import checkerboard, circle, gaussian
4948
50- torchoptics.set_default_spacing(10e-6) # 10 µm grid spacing
49+ torchoptics.set_default_spacing(10e-6) # 10 µm grid spacing
5150 torchoptics.set_default_wavelength(700e-9) # 700 nm (red light)
5251
5352
@@ -63,8 +62,8 @@ Let's create a field from a circular aperture:
6362.. plot ::
6463 :context: close-figs
6564
66- shape = 500 # 500×500 grid
67- field = Field(circle(shape, radius=1e -3))
65+ shape = 1000 # 1000×1000 grid (10 mm × 10 mm physical extent)
66+ field = Field(circle(shape, radius=2e -3))
6867 field.visualize(title="Circular Aperture (z = 0)")
6968
7069Use :meth: `~torchoptics.Field.propagate_to_z ` to propagate a field through free space. As light
@@ -74,13 +73,13 @@ travels, it diffracts, producing characteristic patterns at different distances:
7473 :context: close-figs
7574
7675 # Near-field (Fresnel) diffraction
77- field.propagate_to_z(0.1 ).visualize(title="z = 0.1 m (Fresnel region)")
76+ field.propagate_to_z(0.5 ).visualize(title="z = 0.5 m (Fresnel region)")
7877
7978.. plot ::
8079 :context: close-figs
8180
82- # Far-field (Fraunhofer) diffraction — the Airy pattern
83- field.propagate_to_z(2 .0).visualize(title="z = 2 .0 m (Fraunhofer region)")
81+ # Far-field (Fraunhofer) diffraction: the Airy pattern
82+ field.propagate_to_z(10 .0).visualize(title="z = 10 .0 m (Fraunhofer region)")
8483
8584Close to the aperture (the Fresnel region), diffraction produces fringes near the edges. Far away
8685(the Fraunhofer region), the wavefront converges to the `Airy pattern
@@ -101,22 +100,25 @@ a quadratic phase factor with a circular aperture to the incident field:
101100
102101.. math ::
103102
104- \mathcal {M}(x, y) = \operatorname {circ}(r ) \cdot
103+ \mathcal {M}(x, y) = \operatorname {circ}\! \left ( \frac {r}{R} \right ) \cdot
105104 \exp \!\left (-i \frac {\pi }{\lambda f}(x^2 + y^2 )\right )
106105
106+ where :math: `r = \sqrt {x^2 + y^2 }`, :math: `R` is the aperture radius (half the lens's
107+ physical extent), :math: `\lambda ` is the wavelength, and :math: `f` is the focal length.
108+
107109Calling an element on a field (``lens(field) ``) applies this transformation. Let's focus a Gaussian
108- beam with a 200 mm lens:
110+ beam with a 400 mm lens:
109111
110112.. plot ::
111113 :context: close-figs
112114
113- gaussian_beam = Field(gaussian(shape, waist_radius=1.5e -3))
115+ gaussian_beam = Field(gaussian(shape, waist_radius=3e -3))
114116 gaussian_beam.visualize(title="Gaussian Beam (z = 0)")
115117
116118.. plot ::
117119 :context: close-figs
118120
119- f = 200e-3 # Focal length: 200 mm
121+ f = 1 # Focal length: 1 m
120122 lens = Lens(shape, f, z=0)
121123
122124 focused = lens(gaussian_beam).propagate_to_z(f)
@@ -134,21 +136,23 @@ Use :meth:`~torchoptics.System.measure_at_z` to compute the field at any :math:`
134136
135137As an example, let's build a `4f system
136138<https://en.wikipedia.org/wiki/Fourier_optics#4F_Correlator> `_: two lenses separated by
137- :math: `2 f` with a spatial filter at the Fourier plane. Here we place a high-pass filter that
139+ :math: `2 f` with a spatial filter at the Fourier plane (:math: `z = 2 f`). The system relays the
140+ input image to the output plane (:math: `z = 4 f`), while the Fourier plane in between gives direct
141+ access to the spatial frequency content for filtering. Here we place a high-pass filter that
138142blocks low spatial frequencies, extracting edges from a checkerboard:
139143
140144.. plot ::
141145 :context: close-figs
142146
143- input_field = Field(checkerboard(shape, tile_length=200e -6, num_tiles=15))
147+ input_field = Field(checkerboard(shape, tile_length=400e -6, num_tiles=15))
144148 input_field.visualize(title="Input Field", vmax=1)
145149
146150.. plot ::
147151 :context: close-figs
148152
149153 # High-pass filter at the Fourier plane (z = 2f)
150- f = 100e -3 # Focal length: 100 mm
151- filter_mask = 1 - circle(shape, radius=300e -6)
154+ f = 200e -3 # Focal length: 200 mm
155+ filter_mask = 1 - circle(shape, radius=500e -6)
152156 aperture = AmplitudeModulator(filter_mask, z=2 * f)
153157
154158 aperture.visualize(title="High-Pass Filter at Fourier Plane")
@@ -195,9 +199,7 @@ you can optimize optical designs using gradient descent, the same approach used
195199networks.
196200
197201As an example, let's train a diffractive system to reshape a Gaussian beam into an eight-petal
198- beam. The system consists of three :class: `~torchoptics.elements.PhaseModulator ` layers, each
199- initialized with a flat (zero) phase that will be optimized as a learnable
200- :class: `~torch.nn.Parameter `:
202+ beam. First, we define the input and target fields:
201203
202204.. plot ::
203205 :context: reset
@@ -219,31 +221,41 @@ initialized with a flat (zero) phase that will be optimized as a learnable
219221 input_field = Field(gaussian(shape, waist_radius=waist_radius), z=0)
220222 input_field.visualize(title="Input: Gaussian")
221223
224+ The target is a superposition of two Laguerre-Gaussian modes with opposite orbital angular momentum:
225+
226+ .. math ::
227+
228+ \psi _\text {target} = \mathrm {LG}_0 ^{+4 } + \mathrm {LG}_0 ^{-4 }
229+
230+ whose interference produces an eight-petal intensity pattern.
231+
222232.. plot ::
223233 :context: close-figs
224234
225- # Target: eight-petal beam (LG_0^4 + LG_0^{-4} superposition)
235+ # Target: eight-petal beam (LG_0^{+4} + LG_0^{-4} superposition)
226236 target_data = laguerre_gaussian(shape, p=0, l=4, waist_radius=waist_radius) \
227237 + laguerre_gaussian(shape, p=0, l=-4, waist_radius=waist_radius)
228- target_field = Field(target_data, z=0.8).normalize()
238+ target_field = Field(target_data, z=0.8).normalize() # normalize to unit power
229239 target_field.visualize(title="Target: Petal Beam")
230240
241+ The loss is :math: `1 - |\eta |^2 `, where :math: `\eta ` is the inner product (mode overlap) between the
242+ output and target fields: equal to 1 when they are identical and 0 when orthogonal.
243+
231244.. plot ::
232245 :context: close-figs
233246
234- # Trainable diffractive system
247+ # Trainable diffractive system: three phase planes initialized to zero
235248 system = System(
236249 PhaseModulator(Parameter(torch.zeros(shape, shape)), z=0.2),
237250 PhaseModulator(Parameter(torch.zeros(shape, shape)), z=0.4),
238251 PhaseModulator(Parameter(torch.zeros(shape, shape)), z=0.6),
239252 )
240253
241- # The loss function maximizes the squared mode overlap |<output|target>|^2
242254 optimizer = torch.optim.Adam(system.parameters(), lr=0.05)
243255 for iteration in range(200):
244256 optimizer.zero_grad()
245257 output = system.measure_at_z(input_field, z=0.8)
246- loss = 1 - output.inner(target_field).abs().square()
258+ loss = 1 - output.inner(target_field).abs().square() # 1 - | η|²
247259 loss.backward()
248260 optimizer.step()
249261
@@ -260,13 +272,13 @@ elements, custom loss functions, and joint optimization with neural networks.
260272
261273.. tip ::
262274
263- See the :doc: `training examples </examples/training /index >` for complete inverse design
275+ See the :doc: `optimization examples </examples/optimization /index >` for complete inverse design
264276 workflows with loss curves and animations.
265277
266278
267279Next Steps
268280-----------
269281
270- - :doc: `/examples/index ` — Diffraction, polarization, spatial coherence, and inverse design examples.
271282- :doc: `/user-guide/index ` — In-depth guides on fields, elements, and systems.
283+ - :doc: `/examples/index ` — Diffraction, polarization, spatial coherence, and inverse design examples.
272284- :doc: `/api-reference/index ` — Complete API documentation.
0 commit comments