Skip to content

Commit 52d9eef

Browse files
Update docs and examples (#91)
* Update docs and examples * Update ruff configuration to ignore E741 and add noqa comments in laguerre_gaussian files
1 parent a3062a6 commit 52d9eef

40 files changed

Lines changed: 3183 additions & 388 deletions

README.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ Learn more in our [paper on arXiv](https://arxiv.org/abs/2411.18591).
1919

2020
## Key Features
2121

22-
- 🌊 **Differentiable Wave Optics** Model, analyze, and optimize optical systems using Fourier optics.
23-
- 🔥 **Built on PyTorch** GPU acceleration, batch processing, and automatic differentiation.
24-
- 🛠️ **End-to-End Optimization** Joint optimization of optical hardware and machine learning models.
25-
- 🔬 **Optical Elements** Lenses, phase/amplitude modulators, detectors, polarizers, and more.
26-
- 🖼️ **Spatial Profiles** Hermite-Gaussian, Laguerre-Gaussian, Zernike modes, gratings, and others.
27-
- 🔆 **Polarization and Coherence** Simulate polarized light and fields with arbitrary spatial coherence.
22+
- 🌊 **Differentiable Wave Optics:** Model, analyze, and optimize optical systems using Fourier optics.
23+
- 🔥 **Built on PyTorch:** GPU acceleration, batch processing, and automatic differentiation.
24+
- 🛠️ **End-to-End Optimization:** Joint optimization of optical hardware and machine learning models.
25+
- 🔬 **Optical Elements:** Lenses, phase/amplitude modulators, detectors, polarizers, and more.
26+
- 🖼️ **Spatial Profiles:** Hermite-Gaussian, Laguerre-Gaussian, Zernike modes, gratings, and others.
27+
- 🔆 **Polarization and Coherence:** Simulate polarized light and fields with arbitrary spatial coherence.
2828

2929

3030
## Installation
@@ -41,7 +41,7 @@ Full documentation is available at [torchoptics.readthedocs.io](https://torchopt
4141

4242
### Wave Propagation
4343

44-
Simulate free-space propagation of an octagonal aperture:
44+
Simulate free-space propagation of an octagonal aperture ([full example](https://torchoptics.readthedocs.io/en/stable/examples/optical_phenomena/animate_propagation.html)):
4545

4646
```python
4747
import torch
@@ -65,8 +65,7 @@ for z in torch.linspace(0, 2, 11):
6565

6666
### 4f Imaging System
6767

68-
Simulate a 4f system with a high-pass spatial filter:
69-
68+
Simulate a 4f system with a high-pass spatial filter ([full example](https://torchoptics.readthedocs.io/en/stable/examples/optical_systems/4f_system.html)):
7069
```python
7170
import torch
7271
import torchoptics
@@ -99,7 +98,7 @@ for i in range(5):
9998

10099
### Inverse Design
101100

102-
Train a diffractive optical system to convert a Gaussian beam into a petal beam:
101+
Train a diffractive optical system to convert a Gaussian beam into a petal beam ([full example](https://torchoptics.readthedocs.io/en/stable/examples/optimization/training_petal_beam.html)):
103102

104103
```python
105104
import torch

docs/source/conf.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,12 @@
5353
"matplotlib_animations": (True, "jshtml"),
5454
"subsection_order": ExplicitOrder(
5555
[
56-
"../../examples/basics",
57-
"../../examples/beam_modes",
56+
"../../examples/optical_phenomena",
5857
"../../examples/optical_systems",
59-
"../../examples/advanced",
58+
"../../examples/aberrations",
59+
"../../examples/coherence_and_polarization",
6060
"../../examples/optimization",
61-
"../../examples/quantum_computing",
62-
"../../examples/quantum_states",
61+
"../../examples/quantum",
6362
]
6463
),
6564
}

docs/source/quickstart/index.rst

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ Before starting, make sure TorchOptics is installed (:ref:`installation`).
1111
Overview
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
1716
fully differentiable, enabling gradient-based optimization of optical designs.
1817

1918
The 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

7069
Use :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

8584
Close 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+
107109
Calling 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

135137
As an example, let's build a `4f system
136138
<https://en.wikipedia.org/wiki/Fourier_optics#4F_Correlator>`_: two lenses separated by
137-
:math:`2f` with a spatial filter at the Fourier plane. Here we place a high-pass filter that
139+
:math:`2f` with a spatial filter at the Fourier plane (:math:`z = 2f`). The system relays the
140+
input image to the output plane (:math:`z = 4f`), 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
138142
blocks 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
195199
networks.
196200

197201
As 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

267279
Next 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.

docs/source/user-guide/configuration.rst

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ Configuration
22
=============
33

44

5-
TorchOptics uses two global defaults**spacing** and **wavelength** that are applied
5+
TorchOptics uses two global defaults, **spacing** and **wavelength**, that are applied
66
whenever a :class:`~torchoptics.Field` or :class:`~torchoptics.elements.Element` is created
7-
without explicitly specifying them. Set these at the start of every script.
7+
without explicitly specifying them.
88

99

1010
Setting Defaults
@@ -14,7 +14,7 @@ Setting Defaults
1414
1515
import torchoptics
1616
17-
torchoptics.set_default_spacing(10e-6) # 10 µm grid spacing
17+
torchoptics.set_default_spacing(10e-6) # 10 µm grid spacing
1818
torchoptics.set_default_wavelength(700e-9) # 700 nm wavelength
1919
2020
Retrieve the current values with the corresponding getters:
@@ -24,8 +24,9 @@ Retrieve the current values with the corresponding getters:
2424
torchoptics.get_default_spacing() # Returns Tensor
2525
torchoptics.get_default_wavelength() # Returns Tensor
2626
27-
If you create a field or element without setting defaults first (and without passing ``spacing``
28-
or ``wavelength`` explicitly), a ``ValueError`` is raised.
27+
Set defaults at the start of every script, before creating any fields or elements. If you create
28+
a field or element without setting defaults first (and without passing ``spacing`` or
29+
``wavelength`` explicitly), a ``ValueError`` is raised.
2930

3031

3132
Spacing
@@ -54,9 +55,10 @@ Spacing can be **isotropic** (scalar) or **anisotropic** (2-element tuple):
5455
Wavelength
5556
----------
5657

57-
The wavelength :math:`\lambda` sets the monochromatic operating wavelength used by propagation
58-
methods, wavelength-dependent elements like :class:`~torchoptics.elements.Lens`, and profile
59-
functions like :func:`~torchoptics.profiles.gaussian`.
58+
The wavelength :math:`\lambda` sets the monochromatic operating wavelength for any
59+
:class:`~torchoptics.Field` created without an explicit ``wavelength`` argument. It is used by
60+
propagation algorithms, wavelength-dependent elements like :class:`~torchoptics.elements.Lens`,
61+
and beam profile functions like :func:`~torchoptics.profiles.gaussian`.
6062

6163

6264
Per-Object Overrides
@@ -83,6 +85,8 @@ accuracy for phase-sensitive wave optics. Switch to single precision for faster
8385

8486
.. code-block:: python
8587
88+
import torch
89+
8690
torchoptics.set_default_dtype(torch.float32)
8791
8892
Only ``torch.float32`` and ``torch.float64`` are supported.

0 commit comments

Comments
 (0)